[
  {
    "path": "README",
    "content": "Experimental QUIC support for nginx\n-----------------------------------\n\n1. Introduction\n2. Installing\n3. Configuration\n4. Clients\n5. Troubleshooting\n6. Contributing\n7. Links\n\n1. Introduction\n\n    This is an experimental QUIC [1] / HTTP/3 [2] support for nginx.\n\n    The code is developed in a separate \"quic\" branch available\n    at https://hg.nginx.org/nginx-quic.  Currently it is based\n    on nginx mainline 1.21.x.  We merge new nginx releases into\n    this branch regularly.\n\n    The project code base is under the same BSD license as nginx.\n\n    The code is currently at a beta level of quality and should not\n    be used in production.\n\n    We are working on improving HTTP/3 support with the goal of\n    integrating it to the main NGINX codebase.  Expect frequent\n    updates of this code and don't rely on it for whatever purpose.\n\n    We'll be grateful for any feedback and code submissions however\n    we don't bear any responsibilities for any issues with this code.\n\n    You can always contact us via nginx-devel mailing list [3].\n\n    What works now:\n\n    Currently we support IETF-QUIC draft-29 through final RFC documents.\n    Earlier drafts are NOT supported as they have incompatible wire format.\n\n    nginx should be able to respond to HTTP/3 requests over QUIC and\n    it should be possible to upload and download big files without errors.\n\n    + The handshake completes successfully\n    + One endpoint can update keys and its peer responds correctly\n    + 0-RTT data is being received and acted on\n    + Connection is established using TLS Resume Ticket\n    + A handshake that includes a Retry packet completes successfully\n    + Stream data is being exchanged and ACK'ed\n    + An H3 transaction succeeded\n    + One or both endpoints insert entries into dynamic table and\n      subsequently reference them from header blocks\n    + Version Negotiation packet is sent to client with unknown version\n    + Lost packets are detected and retransmitted properly\n    + Clients may migrate to new address\n\n     Not (yet) supported features:\n\n    - Explicit Congestion Notification (ECN) as specified in quic-recovery [5]\n    - A connection with the spin bit succeeds and the bit is spinning\n    - Structured Logging\n\n    Since the code is experimental and still under development,\n    a lot of things may not work as expected, for example:\n\n    - Flow control mechanism is basic and intended to avoid CPU hog and make\n      simple interactions possible\n\n    - Not all protocol requirements are strictly followed; some of checks are\n      omitted for the sake of simplicity of initial implementation\n\n2. Installing\n\n    You will need a BoringSSL [4] library that provides QUIC support\n\n    $ hg clone -b quic https://hg.nginx.org/nginx-quic\n    $ cd nginx-quic\n    $ ./auto/configure --with-debug --with-http_v3_module       \\\n                       --with-cc-opt=\"-I../boringssl/include\"   \\\n                       --with-ld-opt=\"-L../boringssl/build/ssl  \\\n                                      -L../boringssl/build/crypto\"\n    $ make\n\n    When configuring nginx, you can enable QUIC and HTTP/3 using the\n    following new configuration options:\n\n        --with-http_v3_module     - enable QUIC and HTTP/3\n        --with-http_quic_module   - enable QUIC for older HTTP versions\n        --with-stream_quic_module - enable QUIC in Stream\n\n3. Configuration\n\n    The HTTP \"listen\" directive got two new options: \"http3\" and \"quic\".\n    The \"http3\" option enables HTTP/3 over QUIC on the specified port.\n    The \"quic\" option enables QUIC for older HTTP versions on this port.\n\n    The Stream \"listen\" directive got a new option \"quic\" which enables\n    QUIC as client transport protocol instead of TCP or plain UDP.\n\n    Along with \"http3\" or \"quic\", you also have to specify \"reuseport\"\n    option [6] to make it work properly with multiple workers.\n\n    A number of directives were added that specify transport parameter values:\n\n        quic_max_idle_timeout\n        quic_max_ack_delay\n        quic_max_udp_payload_size\n        quic_initial_max_data\n        quic_initial_max_stream_data_bidi_local\n        quic_initial_max_stream_data_bidi_remote\n        quic_initial_max_stream_data_uni\n        quic_initial_max_streams_bidi\n        quic_initial_max_streams_uni\n        quic_ack_delay_exponent\n        quic_disable_active_migration\n        quic_active_connection_id_limit\n\n    To enable address validation:\n\n        quic_retry on;\n\n    To enable 0-RTT:\n\n        ssl_early_data on;\n\n    Make sure that TLS 1.3 is configured which is required for QUIC:\n\n        ssl_protocols TLSv1.3;\n\n    To enable GSO (Generic Segmentation Offloading):\n\n        quic_gso on;\n\n    By default this Linux-specific optimization [8] is disabled.\n    Enable if your network interface is configured to support GSO.\n\n    A number of directives were added that configure HTTP/3:\n\n        http3_max_table_capacity\n        http3_max_blocked_streams\n        http3_max_concurrent_pushes\n        http3_push\n        http3_push_preload\n\n    An additional variable is available: $quic.\n    The value of $quic is \"quic\" if QUIC connection is used,\n    or an empty string otherwise.\n\nExample configuration:\n\n    http {\n        log_format quic '$remote_addr - $remote_user [$time_local] '\n                        '\"$request\" $status $body_bytes_sent '\n                        '\"$http_referer\" \"$http_user_agent\" \"$quic\"';\n\n        access_log logs/access.log quic;\n\n        server {\n            # for better compatibility it's recommended\n            # to use the same port for quic and https\n            listen 8443 http3 reuseport;\n            listen 8443 ssl;\n\n            ssl_certificate     certs/example.com.crt;\n            ssl_certificate_key certs/example.com.key;\n            ssl_protocols       TLSv1.3;\n\n            location / {\n                # required for browsers to direct them into quic port\n                add_header Alt-Svc 'h3=\":8443\"; ma=86400';\n            }\n        }\n    }\n\n4. Clients\n\n    * Browsers\n\n        Known to work: Firefox 80+ and Chrome 85+ (QUIC draft 29+)\n\n        Beware of strange issues: sometimes browser may decide to ignore QUIC\n        Cache clearing/restart might help.  Always check access.log and\n        error.log to make sure you are using HTTP/3 and not TCP https.\n\n        + to enable QUIC in Firefox, set the following in 'about:config':\n          network.http.http3.enabled = true\n\n        + to enable QUIC in Chrome, enable it on command line and force it\n          on your site:\n\n        $ ./chrome --enable-quic --quic-version=h3-29 \\\n                       --origin-to-force-quic-on=example.com:8443\n\n    * Console clients\n\n        Known to work: ngtcp2, firefox's neqo and chromium's console clients:\n\n        $ examples/client 127.0.0.1 8443 https://example.com:8443/index.html\n\n        $ ./neqo-client https://127.0.0.1:8443/\n\n        $ chromium-build/out/my_build/quic_client http://example.com:8443 \\\n                  --quic_version=h3-29 \\\n                  --allow_unknown_root_cert \\\n                  --disable_certificate_verification\n\n\n   If you've got it right, in the access log you should see something like:\n\n   127.0.0.1 - - [24/Apr/2020:11:27:29 +0300] \"GET / HTTP/3\" 200 805 \"-\"\n                                         \"nghttp3/ngtcp2 client\" \"quic\"\n\n\n5. Troubleshooting\n\n    Here are some tips that may help you to identify problems:\n\n    + Ensure you are building with proper SSL library that supports QUIC\n\n    + Ensure you are using the proper SSL library in runtime\n      (`nginx -V` will show you what you are using)\n\n    + Ensure your client is actually sending QUIC requests\n      (see \"Clients\" section about browsers and cache)\n\n      We recommend to start with simple console client like ngtcp2\n      to ensure you've got server configured properly before trying\n      with real browsers that may be very picky with certificates,\n      for example.\n\n    + Build nginx with debug support [7] and check your debug log.\n      It should contain all details about connection and why it\n      failed. All related messages contain \"quic \" prefix and can\n      be easily filtered out.\n\n    + If you want to investigate deeper, you may want to enable\n      additional debugging in src/event/quic/ngx_event_quic_connection.h:\n\n        #define NGX_QUIC_DEBUG_PACKETS\n        #define NGX_QUIC_DEBUG_FRAMES\n        #define NGX_QUIC_DEBUG_ALLOC\n        #define NGX_QUIC_DEBUG_CRYPTO\n\n6. Contributing\n\n    If you are willing to contribute, please refer to\n    http://nginx.org/en/docs/contributing_changes.html\n\n7. Links\n\n    [1] https://datatracker.ietf.org/doc/html/rfc9000\n    [2] https://datatracker.ietf.org/doc/html/draft-ietf-quic-http\n    [3] https://mailman.nginx.org/mailman/listinfo/nginx-devel\n    [4] https://boringssl.googlesource.com/boringssl/\n    [5] https://datatracker.ietf.org/doc/html/rfc9002\n    [6] https://nginx.org/en/docs/http/ngx_http_core_module.html#listen\n    [7] https://nginx.org/en/docs/debugging_log.html\n    [8] http://vger.kernel.org/lpc_net2018_talks/willemdebruijn-lpc2018-udpgso-paper-DRAFT-1.pdf\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 'Compiler Version' 2>&1 \\\n                                 | sed -e 's/^.* Version \\(.*\\)/\\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# 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\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"
  },
  {
    "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/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"
  },
  {
    "path": "auto/lib/openssl/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $OPENSSL != NONE ]; then\n\n    have=NGX_OPENSSL . auto/have\n    have=NGX_SSL . auto/have\n\n    if [ $USE_OPENSSL_QUIC = YES ]; then\n        have=NGX_QUIC . auto/have\n    fi\n\n    case \"$CC\" in\n\n        cl | bcc32)\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            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\n    if [ $USE_OPENSSL_QUIC = YES ]; then\n\n        ngx_feature=\"OpenSSL QUIC support\"\n        ngx_feature_name=\"NGX_QUIC\"\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_set_quic_method(NULL, NULL)\"\n        . auto/feature\n\n        if [ $ngx_found = no ]; then\n\ncat << END\n\n$0: error: certain modules require OpenSSL QUIC support.\nYou can either do not enable the modules, or install the OpenSSL library with\nQUIC support into the system, or build the OpenSSL library with QUIC support\nstatically from the source with nginx by using --with-openssl=<path> option.\n\nEND\n            exit 1\n        fi\n    fi\nfi\n"
  },
  {
    "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        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\"\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 VC-WIN32 no-shared\t\t\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    CORE_INCS=\"$CORE_INCS $PCRE\"\n\n    case \"$NGX_CC_NAME\" in\n\n        msvc | owc | bcc)\n            have=NGX_PCRE . auto/have\n            have=PCRE_STATIC . auto/have\n            CORE_DEPS=\"$CORE_DEPS $PCRE/pcre.h\"\n            LINK_DEPS=\"$LINK_DEPS $PCRE/pcre.lib\"\n            CORE_LIBS=\"$CORE_LIBS $PCRE/pcre.lib\"\n        ;;\n\n        icc)\n            have=NGX_PCRE . auto/have\n            CORE_DEPS=\"$CORE_DEPS $PCRE/pcre.h\"\n\n            LINK_DEPS=\"$LINK_DEPS $PCRE/.libs/libpcre.a\"\n\n            echo $ngx_n \"checking for PCRE library ...$ngx_c\"\n\n            if [ -f $PCRE/pcre.h ]; then\n                ngx_pcre_ver=`grep PCRE_MAJOR $PCRE/pcre.h \\\n                              | sed -e 's/^.*PCRE_MAJOR.* \\(.*\\)$/\\1/'`\n\n            else if [ -f $PCRE/configure.in ]; then\n                ngx_pcre_ver=`grep PCRE_MAJOR= $PCRE/configure.in \\\n                              | sed -e 's/^.*=\\(.*\\)$/\\1/'`\n\n            else\n                ngx_pcre_ver=`grep pcre_major, $PCRE/configure.ac \\\n                              | sed -e 's/^.*pcre_major,.*\\[\\(.*\\)\\].*$/\\1/'`\n            fi\n            fi\n\n            echo \" $ngx_pcre_ver major version found\"\n\n            # to allow -ipo optimization we link with the *.o but not library\n\n            case \"$ngx_pcre_ver\" in\n                4|5)\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre.o\"\n                ;;\n\n                6)\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_chartables.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_compile.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_exec.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_fullinfo.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_globals.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_tables.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_try_flipped.o\"\n                ;;\n\n                *)\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_chartables.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_compile.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_exec.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_fullinfo.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_globals.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_tables.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_try_flipped.o\"\n                    CORE_LIBS=\"$CORE_LIBS $PCRE/pcre_newline.o\"\n                ;;\n\n            esac\n        ;;\n\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_DEPS=\"$CORE_DEPS $PCRE/pcre.h\"\n            LINK_DEPS=\"$LINK_DEPS $PCRE/.libs/libpcre.a\"\n            CORE_LIBS=\"$CORE_LIBS $PCRE/.libs/libpcre.a\"\n        ;;\n\n    esac\n\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\n        PCRE=NO\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        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\ncase \"$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\\\" | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n    ;;\n\n    *)\n        ngx_makefile=\n    ;;\n\nesac\n\n\nif [ -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\nelse\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\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/event/quic \\\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/v3 \\\n         $NGX_OBJS/src/http/modules $NGX_OBJS/src/http/modules/perl \\\n         $NGX_OBJS/src/mail \\\n         $NGX_OBJS/src/stream \\\n         $NGX_OBJS/src/misc\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\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    # 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_v3_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_v3_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 [ $HTTP_V3 = YES ]; then\n        ngx_module_name=ngx_http_v3_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/v3/ngx_http_v3_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_V3\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_huff_decode.c \\\n                         src/http/v2/ngx_http_v2_huff_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 [ $HTTP_V3 = YES ]; then\n        have=NGX_HTTP_V3 . auto/have\n        have=NGX_HTTP_HEADERS . auto/have\n        HTTP_QUIC=YES\n\n        ngx_module_name=ngx_http_v3_module\n        ngx_module_incs=src/http/v3\n        ngx_module_deps=\"src/http/v3/ngx_http_v3.h \\\n                         src/http/v3/ngx_http_v3_encode.h \\\n                         src/http/v3/ngx_http_v3_parse.h \\\n                         src/http/v3/ngx_http_v3_tables.h \\\n                         src/http/v3/ngx_http_v3_streams.h\"\n        ngx_module_srcs=\"src/http/v3/ngx_http_v3.c \\\n                         src/http/v3/ngx_http_v3_encode.c \\\n                         src/http/v3/ngx_http_v3_parse.c \\\n                         src/http/v3/ngx_http_v3_tables.c \\\n                         src/http/v3/ngx_http_v3_streams.c \\\n                         src/http/v3/ngx_http_v3_request.c \\\n                         src/http/v3/ngx_http_v3_module.c\"\n        ngx_module_libs=\n        ngx_module_link=$HTTP_V3\n\n        if [ $HTTP_V2 = NO ]; then\n            ngx_module_srcs=\"$ngx_module_srcs \\\n                             src/http/v2/ngx_http_v2_huff_decode.c \\\n                             src/http/v2/ngx_http_v2_huff_encode.c\"\n        fi\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_QUIC = YES ]; then\n        USE_OPENSSL_QUIC=YES\n        have=NGX_HTTP_QUIC . auto/have\n        HTTP_SSL=YES\n\n        ngx_module_name=ngx_http_quic_module\n        ngx_module_incs=\n        ngx_module_deps=src/http/modules/ngx_http_quic_module.h\n        ngx_module_srcs=src/http/modules/ngx_http_quic_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_QUIC\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_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\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_QUIC = YES ]; then\n        USE_OPENSSL_QUIC=YES\n        have=NGX_STREAM_QUIC . auto/have\n        STREAM_SSL=YES\n\n        ngx_module_name=ngx_stream_quic_module\n        ngx_module_deps=src/stream/ngx_stream_quic_module.h\n        ngx_module_srcs=src/stream/ngx_stream_quic_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_QUIC\n\n        . auto/module\n    fi\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\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_OPENSSL_QUIC = YES ]; then\n    ngx_module_type=CORE\n    ngx_module_name=ngx_quic_module\n    ngx_module_incs=\n    ngx_module_deps=\"src/event/quic/ngx_event_quic.h \\\n                     src/event/quic/ngx_event_quic_transport.h \\\n                     src/event/quic/ngx_event_quic_protection.h \\\n                     src/event/quic/ngx_event_quic_connection.h \\\n                     src/event/quic/ngx_event_quic_frames.h \\\n                     src/event/quic/ngx_event_quic_connid.h \\\n                     src/event/quic/ngx_event_quic_migration.h \\\n                     src/event/quic/ngx_event_quic_streams.h \\\n                     src/event/quic/ngx_event_quic_ssl.h \\\n                     src/event/quic/ngx_event_quic_tokens.h \\\n                     src/event/quic/ngx_event_quic_ack.h \\\n                     src/event/quic/ngx_event_quic_output.h \\\n                     src/event/quic/ngx_event_quic_socket.h \\\n                     src/event/quic/ngx_event_quic_mtu.h\"\n    ngx_module_srcs=\"src/event/quic/ngx_event_quic.c \\\n                     src/event/quic/ngx_event_quic_transport.c \\\n                     src/event/quic/ngx_event_quic_protection.c \\\n                     src/event/quic/ngx_event_quic_frames.c \\\n                     src/event/quic/ngx_event_quic_connid.c \\\n                     src/event/quic/ngx_event_quic_migration.c \\\n                     src/event/quic/ngx_event_quic_streams.c \\\n                     src/event/quic/ngx_event_quic_ssl.c \\\n                     src/event/quic/ngx_event_quic_tokens.c \\\n                     src/event/quic/ngx_event_quic_ack.c \\\n                     src/event/quic/ngx_event_quic_output.c \\\n                     src/event/quic/ngx_event_quic_socket.c \\\n                     src/event/quic/ngx_event_quic_mtu.c\"\n\n    ngx_module_libs=\n    ngx_module_link=YES\n    ngx_module_order=\n\n    . auto/module\n\n    if [ $NGX_QUIC_BPF$BPF_FOUND$SO_COOKIE_FOUND = YESYESYES ]; then\n        ngx_module_type=CORE\n        ngx_module_name=ngx_quic_bpf_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=\"src/event/quic/ngx_event_quic_bpf.c \\\n                         src/event/quic/ngx_event_quic_bpf_code.c\"\n        ngx_module_libs=\n        ngx_module_link=YES\n        ngx_module_order=\n\n        . auto/module\n\n        have=NGX_QUIC_BPF . auto/have\n    fi\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\nmodules=\"$CORE_MODULES $EVENT_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"
  },
  {
    "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_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\nNGX_QUIC_BPF=YES\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=NO\nHTTP_QUIC=NO\nHTTP_V2=NO\nHTTP_V3=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=NO\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_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_ZONE=YES\n\n# STUB\nHTTP_STUB_STATUS=NO\n\nMAIL=NO\nMAIL_SSL=NO\nMAIL_POP3=YES\nMAIL_IMAP=YES\nMAIL_SMTP=YES\n\nSTREAM=NO\nSTREAM_SSL=NO\nSTREAM_QUIC=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\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\n\nUSE_OPENSSL=NO\nUSE_OPENSSL_QUIC=NO\nOPENSSL=NONE\n\nUSE_ZLIB=NO\nZLIB=NONE\nZLIB_OPT=\nZLIB_ASM=NO\n\nUSE_PERL=NO\nNGX_PERL=perl\n\nUSE_LIBXSLT=NO\nUSE_LIBGD=NO\nUSE_GEOIP=NO\n\nNGX_GOOGLE_PERFTOOLS=NO\nNGX_CPP_TEST=NO\n\nBPF_FOUND=NO\nSO_COOKIE_FOUND=NO\n\nNGX_LIBATOMIC=NO\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        --with-file-aio)                 NGX_FILE_AIO=YES           ;;\n\n        --without-quic_bpf_module)       NGX_QUIC_BPF=NO            ;;\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_quic_module)         HTTP_QUIC=YES              ;;\n        --with-http_v2_module)           HTTP_V2=YES                ;;\n        --with-http_v3_module)           HTTP_V3=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        # 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_quic_module)       STREAM_QUIC=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        --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\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\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        --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  --with-threads                     enable thread pool support\n\n  --with-file-aio                    enable file AIO support\n\n  --without-quic_bpf_module          disable ngx_quic_bpf_module\n\n  --with-http_ssl_module             enable ngx_http_ssl_module\n  --with-http_quic_module            enable ngx_http_quic_module\n  --with-http_v2_module              enable ngx_http_v2_module\n  --with-http_v3_module              enable ngx_http_v3_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_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_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  --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-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_quic_module          enable ngx_stream_quic_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  --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\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-openssl=DIR                 set path to OpenSSL library sources\n  --with-openssl-opt=OPTIONS         set additional build options for OpenSSL\n\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 )\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 [ $NGX_FILE_AIO = YES ]; then\n    if [ $osreldate -gt 502103 ]; then\n        echo \" + sendfile()'s SF_NODISKIO found\"\n\n        have=NGX_HAVE_AIO_SENDFILE . auto/have\n    fi\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\nCC_AUX_FLAGS=\"$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64\"\n\n\n# (E)BPF\n\nngx_feature=\"BPF support\"\nngx_feature_name=\"NGX_HAVE_BPF\"\nngx_feature_run=no\nngx_feature_incs=\"#include <linux/bpf.h>\n                  #include <sys/syscall.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"\n    union bpf_attr attr = { 0 };\n    /* only declare BPF support if all required features found */\n    attr.map_flags = 0;\n    attr.map_type = BPF_MAP_TYPE_SOCKHASH;\n    syscall(__NR_bpf, 0, &attr, 0);\"\n\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    BPF_FOUND=YES\n\n    CORE_SRCS=\"$CORE_SRCS src/core/ngx_bpf.c\"\n    CORE_DEPS=\"$CORE_DEPS src/core/ngx_bpf.h\"\nfi\n\n\n# SO_COOKIE socket option\n\nngx_feature=\"SO_COOKIE\"\nngx_feature_name=\"NGX_HAVE_SO_COOKIE\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <stdint.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"socklen_t optlen = sizeof(uint64_t);\n              uint64_t cookie;\n              getsockopt(0, SOL_SOCKET, SO_COOKIE, &cookie, &optlen)\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    SO_COOKIE_FOUND=YES\n    have=NGX_HAVE_SO_COOKIE . auto/have\nfi\n\n\n# UDP_SEGMENT socket option is used for segmentation offloading\n\nngx_feature=\"UDP_SEGMENT\"\nngx_feature_name=\"NGX_HAVE_UDP_SEGMENT\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <stdint.h>\n                  #include <netinet/udp.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int fd = socket(AF_INET, SOCK_DGRAM, 0);\n              int on = 1;\n              if (setsockopt(fd, SOL_UDP, 103, &on, sizeof(on)) == -1) return 1;\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    UDP_SEGMENT_FOUND=YES\n    have=NGX_HAVE_UDP_SEGMENT . auto/have\n    have=UDP_SEGMENT value=103 . auto/define\nfi\n\n\n# ngx_quic_bpf module uses sockhash to select socket from reuseport group,\n# support appeared in Linux-5.7:\n#\n# commit: 9fed9000c5c6cacfcaaa48aff74818072ae294cc\n# bpf: Allow selecting reuseport socket from a SOCKMAP/SOCKHASH\n#\nif [ $NGX_QUIC_BPF$BPF_FOUND = YESYES ]; then\n    echo $ngx_n \"checking for kernel with reuseport/BPF support...$ngx_c\"\n    if [ $version -lt 329472 ]; then\n        echo \" not found (at least 5.7 is required)\"\n        NGX_QUIC_BPF=NO\n    else\n        echo \" found\"\n    fi\nfi\n\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_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_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 src/event/quic\"\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            src/event/ngx_event_udp.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\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\nFILE_AIO_SRCS=\"src/os/unix/ngx_file_aio_read.c\"\nLINUX_AIO_SRCS=\"src/os/unix/ngx_linux_aio_read.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_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_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"
  },
  {
    "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\" ;;\n        NONE)  echo \"  + PCRE library is not used\" ;;\n        *)     echo \"  + using PCRE 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\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=\"IP_MTU_DISCOVER\"\nngx_feature_name=\"NGX_HAVE_IP_MTU_DISCOVER\"\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_MTU_DISCOVER, NULL, 0)\"\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\nngx_feature=\"recvmmsg()\"\nngx_feature_name=\"NGX_HAVE_UDP_RECVMMSG\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct mmsghdr msgs[1];\n              recvmmsg(-1, msgs, 1, 0, NULL);\"\n. auto/feature\n\n\nngx_feature=\"sendmmsg()\"\nngx_feature_name=\"NGX_HAVE_UDP_SENDMMSG\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct mmsghdr msgs[1];\n              sendmmsg(-1, msgs, 1, 0);\"\n. auto/feature\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\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\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\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        # 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\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": "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/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/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_delay\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_jwt_type\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_conf_command\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_body_filter\nsyn keyword ngxDirective contained js_content\nsyn keyword ngxDirective contained js_filter\nsyn keyword ngxDirective contained js_header_filter\nsyn keyword ngxDirective contained js_import\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 js_var\nsyn keyword ngxDirective contained keepalive\nsyn keyword ngxDirective contained keepalive_disable\nsyn keyword ngxDirective contained keepalive_requests\nsyn keyword ngxDirective contained keepalive_time\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_dry_run\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_errors\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_flags\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_smtp_auth\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_conf_command\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_conf_command\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_ocsp\nsyn keyword ngxDirective contained ssl_ocsp_cache\nsyn keyword ngxDirective contained ssl_ocsp_responder\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_reject_handshake\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_flags\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_conf_command\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_conf_command\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_map_to_local\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_buffers\nsyn keyword ngxDirectiveThirdParty contained ajp_buffer_size\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_secret\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 handlers_lazy_init\nsyn keyword ngxDirectiveThirdParty contained handler_type\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 log_handler_code\nsyn keyword ngxDirectiveThirdParty contained log_handler_name\nsyn keyword ngxDirectiveThirdParty contained log_handler_property\nsyn keyword ngxDirectiveThirdParty contained log_handler_type\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\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_dotfiles\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_events_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_event_string\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_eventsource_ping_comment\nsyn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_data\nsyn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_event\nsyn keyword ngxDirectiveThirdParty contained nchan_eventsource_ping_interval\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_subscriber\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_compound_etag_message_id\nsyn keyword ngxDirectiveThirdParty contained nchan_subscribe_request\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_first_message\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_http_raw_stream_separator\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_info\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_info_string\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 exit_worker_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained exit_worker_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_thread_cache_max_entries\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_shm_zone_size\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_samesite\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\" nginx-sticky-module-ng\n\" https://github.com/ayty-adrianomartins/nginx-sticky-module-ng\nsyn keyword ngxDirectiveThirdParty contained sticky_no_fallback\n\n\" dynamic linking and call the function of your application\n\" https://github.com/Taymindis/nginx-link-function\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_add_prop\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_add_req_header\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_ca_cert\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_call\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_download_link_lib\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_lib\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_shm_size\nsyn keyword ngxDirectiveThirdParty contained ngx_link_func_subrequest\n\n\" purge content from FastCGI, proxy, SCGI and uWSGI caches\n\" https://github.com/torden/ngx_cache_purge\nsyn keyword ngxDirectiveThirdParty contained cache_purge_response_type\n\n\" set the flags \"HttpOnly\", \"secure\" and \"SameSite\" for cookies\n\" https://github.com/AirisX/nginx_cookie_flag_module\nsyn keyword ngxDirectiveThirdParty contained set_cookie_flag\n\n\" Embed websockify into Nginx (convert any tcp connection into websocket)\n\" https://github.com/tg123/websockify-nginx-module\nsyn keyword ngxDirectiveThirdParty contained websockify_buffer_size\nsyn keyword ngxDirectiveThirdParty contained websockify_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained websockify_pass\nsyn keyword ngxDirectiveThirdParty contained websockify_read_timeout\nsyn keyword ngxDirectiveThirdParty contained websockify_send_timeout\n\n\" IP2Location Nginx\n\" https://github.com/ip2location/ip2location-nginx\nsyn keyword ngxDirectiveThirdParty contained ip2location_addresstype\nsyn keyword ngxDirectiveThirdParty contained ip2location_areacode\nsyn keyword ngxDirectiveThirdParty contained ip2location_category\nsyn keyword ngxDirectiveThirdParty contained ip2location_city\nsyn keyword ngxDirectiveThirdParty contained ip2location_country_long\nsyn keyword ngxDirectiveThirdParty contained ip2location_country_short\nsyn keyword ngxDirectiveThirdParty contained ip2location_domain\nsyn keyword ngxDirectiveThirdParty contained ip2location_elevation\nsyn keyword ngxDirectiveThirdParty contained ip2location_iddcode\nsyn keyword ngxDirectiveThirdParty contained ip2location_isp\nsyn keyword ngxDirectiveThirdParty contained ip2location_latitude\nsyn keyword ngxDirectiveThirdParty contained ip2location_longitude\nsyn keyword ngxDirectiveThirdParty contained ip2location_mcc\nsyn keyword ngxDirectiveThirdParty contained ip2location_mnc\nsyn keyword ngxDirectiveThirdParty contained ip2location_mobilebrand\nsyn keyword ngxDirectiveThirdParty contained ip2location_netspeed\nsyn keyword ngxDirectiveThirdParty contained ip2location_proxy\nsyn keyword ngxDirectiveThirdParty contained ip2location_proxy_recursive\nsyn keyword ngxDirectiveThirdParty contained ip2location_region\nsyn keyword ngxDirectiveThirdParty contained ip2location_timezone\nsyn keyword ngxDirectiveThirdParty contained ip2location_usagetype\nsyn keyword ngxDirectiveThirdParty contained ip2location_weatherstationcode\nsyn keyword ngxDirectiveThirdParty contained ip2location_weatherstationname\nsyn keyword ngxDirectiveThirdParty contained ip2location_zipcode\n\n\" IP2Proxy module for Nginx\n\" https://github.com/ip2location/ip2proxy-nginx\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_as\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_asn\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_city\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_country_long\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_country_short\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_database\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_domain\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_isp\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_is_proxy\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_last_seen\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_provider\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_proxy\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_recursive\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_proxy_type\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_region\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_threat\nsyn keyword ngxDirectiveThirdParty contained ip2proxy_usage_type\n\n\n\n\" highlight\n\nhi def link ngxComment Comment\nhi def link ngxParamComment Comment\nhi def link ngxListenComment Comment\nhi def link ngxVariable Identifier\nhi def link ngxVariableString PreProc\nhi def link ngxString String\nhi def link ngxListenString String\n\nhi def link ngxBoolean Boolean\nhi def link ngxDirectiveBlock Statement\nhi def link ngxDirectiveImportant Type\nhi def link ngxDirectiveListen Type\nhi def link ngxDirectiveControl Keyword\nhi def link ngxDirectiveError Constant\nhi def link ngxDirectiveDeprecated Error\nhi def link ngxDirective Identifier\nhi def link ngxDirectiveThirdParty Special\nhi def link ngxDirectiveThirdPartyDeprecated Error\n\nhi def link ngxListenOptions Keyword\nhi def link ngxListenOptionsDeprecated Error\n\nlet b:current_syntax = \"nginx\"\n"
  },
  {
    "path": "docs/GNUmakefile",
    "content": "\nVER=\t$(shell grep 'define NGINX_VERSION' src/core/nginx.h\t\t\\\n\t\t| sed -e 's/^.*\"\\(.*\\)\".*/\\1/')\nNGINX=\tnginx-$(VER)\nTEMP=\ttmp\nXSLS?=\txslscript.pl\n\n\nall:\t\tchanges\n\nchanges:\t$(TEMP)/$(NGINX)/CHANGES.ru\t\t\t\t\\\n\t\t$(TEMP)/$(NGINX)/CHANGES\n\n\n$(TEMP)/$(NGINX)/CHANGES.ru:\tdocs/dtd/changes.dtd\t\t\t\\\n\t\t\t\tdocs/xml/nginx/changes.xml\t\t\\\n\t\t\t\tdocs/xml/change_log_conf.xml\t\t\\\n\t\t\t\tdocs/xslt/changes.xslt\n\n\tmkdir -p $(TEMP)/$(NGINX)\n\n\txmllint --noout --valid docs/xml/nginx/changes.xml\n\txsltproc --stringparam lang ru\t\t\t\t\t\\\n\t\t-o $@ docs/xslt/changes.xslt docs/xml/nginx/changes.xml\n\n\n$(TEMP)/$(NGINX)/CHANGES:\tdocs/dtd/changes.dtd\t\t\t\\\n\t\t\t\tdocs/xml/nginx/changes.xml\t\t\\\n\t\t\t\tdocs/xml/change_log_conf.xml\t\t\\\n\t\t\t\tdocs/xslt/changes.xslt\n\n\tmkdir -p $(TEMP)/$(NGINX)\n\n\txmllint --noout --valid docs/xml/nginx/changes.xml\n\txsltproc --stringparam lang en\t\t\t\t\t\\\n\t\t-o $@ docs/xslt/changes.xslt docs/xml/nginx/changes.xml\n\n\ndocs/xslt/changes.xslt:\t\tdocs/xsls/changes.xsls\n\n\t$(XSLS) -o $@ $<\n"
  },
  {
    "path": "docs/dtd/change_log_conf.dtd",
    "content": "\n<!ELEMENT configuration   (length, start, indent, changes+) >\n\n<!ELEMENT length          (#PCDATA) >\n<!ELEMENT start           (#PCDATA) >\n<!ELEMENT indent          (#PCDATA) >\n\n<!ELEMENT changes         (title, length,\n                           bugfix, feature, change, workaround,\n                           (month, month, month, month, month, month,\n                            month, month, month, month, month, month)?) >\n\n<!ATTLIST changes         lang ( ru | en) #REQUIRED>\n\n<!ELEMENT title           (#PCDATA) >\n\n<!ELEMENT bugfix          (#PCDATA) >\n<!ELEMENT feature         (#PCDATA) >\n<!ELEMENT change          (#PCDATA) >\n<!ELEMENT workaround      (#PCDATA) >\n\n<!ELEMENT month           (#PCDATA) >\n"
  },
  {
    "path": "docs/dtd/changes.dtd",
    "content": "\n<!ENTITY  nbsp         \"&#xA0;\" >\n<!ENTITY  mdash        \"&#xA0;- \" >\n\n\n<!ELEMENT change_log   (changes)* >\n<!ATTLIST change_log   title  CDATA #REQUIRED >\n\n<!ELEMENT changes      (change)* >\n<!ATTLIST changes      ver    CDATA #REQUIRED\n                       date   CDATA #REQUIRED\n>\n\n<!ELEMENT change       (para)* >\n<!ATTLIST change       type (bugfix | feature | change | security | workaround) #IMPLIED >\n\n<!ELEMENT para         (#PCDATA | at | br | nobr)* >\n<!ATTLIST para         lang (ru | en) #REQUIRED >\n\n<!ELEMENT at           EMPTY >\n<!ELEMENT br           EMPTY >\n<!ELEMENT nobr         (#PCDATA) >\n"
  },
  {
    "path": "docs/html/50x.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<title>Error</title>\n<style>\nhtml { color-scheme: light dark; }\nbody { width: 35em; margin: 0 auto;\nfont-family: Tahoma, Verdana, Arial, sans-serif; }\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, nginx.</em></p>\n</body>\n</html>\n"
  },
  {
    "path": "docs/html/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\nhtml { color-scheme: light dark; }\nbody { width: 35em; margin: 0 auto;\nfont-family: Tahoma, Verdana, Arial, sans-serif; }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx 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://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n"
  },
  {
    "path": "docs/man/nginx.8",
    "content": ".\\\"\n.\\\" Copyright (C) 2010, 2019 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 November 5, 2020\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 e 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, a mail proxy server, and a generic\nTCP/UDP 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 e Ar file\nUse an alternative error log\n.Ar file .\nSpecial value\n.Cm stderr\nindicates that the standard error output should be used.\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\nSame as\n.Fl t ,\nbut additionally dump configuration files to standard output.\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 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 Mt igor@sysoev.ru .\n.Pp\nThis manual page was originally written by\n.An Sergey A. Osokin Aq Mt osa@FreeBSD.org.ru\nas a result of compiling many\n.Nm\ndocuments from all over the world.\n"
  },
  {
    "path": "docs/text/LICENSE",
    "content": "/* \n * Copyright (C) 2002-2021 Igor Sysoev\n * Copyright (C) 2011-2021 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"
  },
  {
    "path": "docs/text/README",
    "content": "\nDocumentation is available at http://nginx.org\n\n"
  },
  {
    "path": "docs/xml/change_log_conf.xml",
    "content": "<?xml version=\"1.0\" ?>\n<!DOCTYPE configuration SYSTEM \"../dtd/change_log_conf.dtd\" >\n\n<configuration>\n\n<length>76</length>\n\n<start>    *) </start>\n<indent>       </indent>\n\n<changes lang=\"ru\">\n    <title>Изменения в </title>\n    <length>66</length>\n\n    <bugfix>Исправление</bugfix>\n    <feature>Добавление</feature>\n    <change>Изменение</change>\n    <security>Безопасность</security>\n    <workaround>Изменение</workaround>\n</changes>\n\n<changes lang=\"en\">\n    <title>Changes with </title>\n    <length>65</length>\n\n    <bugfix>Bugfix</bugfix>\n    <feature>Feature</feature>\n    <change>Change</change>\n    <security>Security</security>\n    <workaround>Workaround</workaround>\n\n    <month> Jan </month>\n    <month> Feb </month>\n    <month> Mar </month>\n    <month> Apr </month>\n    <month> May </month>\n    <month> Jun </month>\n    <month> Jul </month>\n    <month> Aug </month>\n    <month> Sep </month>\n    <month> Oct </month>\n    <month> Nov </month>\n    <month> Dec </month>\n\n</changes>\n\n</configuration>\n"
  },
  {
    "path": "docs/xml/nginx/changes.xml",
    "content": "<?xml version=\"1.0\" ?>\n<!DOCTYPE change_log SYSTEM \"../../dtd/changes.dtd\" >\n\n\n<change_log title=\"nginx\">\n\n\n<changes ver=\"1.21.4\" date=\"2021-11-02\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nподдержка NPN вместо ALPN для установления HTTP/2-соединений\nупразднена.\n</para>\n<para lang=\"en\">\nsupport for NPN instead of ALPN to establish HTTP/2 connections\nhas been removed.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx закрывает SSL соединение, если клиент использует ALPN,\nно nginx не поддерживает ни один из присланных клиентом протоколов.\n</para>\n<para lang=\"en\">\nnow nginx rejects SSL connections if ALPN is used by the client,\nbut no supported protocols can be negotiated.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв директиве sendfile_max_chunk значение по умолчанию\nизменено на 2 мегабайта.\n</para>\n<para lang=\"en\">\nthe default value of the \"sendfile_max_chunk\" directive\nwas changed to 2 megabytes.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_half_close в модуле stream.\n</para>\n<para lang=\"en\">\nthe \"proxy_half_close\" directive in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_alpn в модуле stream.\n</para>\n<para lang=\"en\">\nthe \"ssl_alpn\" directive in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_alpn_protocol.\n</para>\n<para lang=\"en\">\nthe $ssl_alpn_protocol variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка SSL_sendfile() при использовании OpenSSL 3.0.\n</para>\n<para lang=\"en\">\nsupport for SSL_sendfile() when using OpenSSL 3.0.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива mp4_start_key_frame в модуле ngx_http_mp4_module.<br/>\nСпасибо Tracey Jaquith.\n</para>\n<para lang=\"en\">\nthe \"mp4_start_key_frame\" directive in the ngx_http_mp4_module.<br/>\nThanks to Tracey Jaquith.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв переменной $content_length при использовании chunked transfer encoding.\n</para>\n<para lang=\"en\">\nin the $content_length variable when using chunked transfer encoding.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри получении ответа некорректной длины от проксируемого бэкенда\nnginx мог тем не менее закэшировать соединение.<br/>\nСпасибо Awdhesh Mathpal.\n</para>\n<para lang=\"en\">\nafter receiving a response with incorrect length from a proxied backend\nnginx might nevertheless cache the connection.<br/>\nThanks to Awdhesh Mathpal.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнекорректные заголовки от бэкендов\nлоггировались на уровне info вместо error;\nошибка появилась в 1.21.1.\n</para>\n<para lang=\"en\">\ninvalid headers from backends\nwere logged at the \"info\" level instead of \"error\";\nthe bug had appeared in 1.21.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 и директивы aio_write\nзапросы могли зависать.\n</para>\n<para lang=\"en\">\nrequests might hang\nwhen using HTTP/2 and the \"aio_write\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.21.3\" date=\"2021-09-07\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nоптимизация чтения тела запроса\nпри использовании HTTP/2.\n</para>\n<para lang=\"en\">\noptimization of client request body reading\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво внутреннем API для обработки тела запроса\nпри использовании HTTP/2 и буферизации обрабатываемых данных.\n</para>\n<para lang=\"en\">\nin request body filters internal API\nwhen using HTTP/2 and buffering of the data being processed.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.21.2\" date=\"2021-08-31\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx возвращает ошибку,\nесли в запросе по протоколу HTTP/1.0 присутствует\nстрока заголовка \"Transfer-Encoding\".\n</para>\n<para lang=\"en\">\nnow nginx rejects HTTP/1.0 requests\nwith the \"Transfer-Encoding\" header line.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nэкспортные шифры больше не поддерживаются.\n</para>\n<para lang=\"en\">\nexport ciphers are no longer supported.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nсовместимость с OpenSSL 3.0.\n</para>\n<para lang=\"en\">\nOpenSSL 3.0 compatibility.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь серверу аутентификации почтового прокси-сервера\nпередаются строки заголовка \"Auth-SSL-Protocol\" и \"Auth-SSL-Cipher\".<br/>\nСпасибо Rob Mueller.\n</para>\n<para lang=\"en\">\nthe \"Auth-SSL-Protocol\" and \"Auth-SSL-Cipher\" header lines\nare now passed to the mail proxy authentication server.<br/>\nThanks to Rob Mueller.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nAPI для обработки тела запроса\nтеперь позволяет буферизировать обрабатываемые данные.\n</para>\n<para lang=\"en\">\nrequest body filters API\nnow permits buffering of the data being processed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL-соединения к бэкендам в модуле stream\nмогли зависать после SSL handshake.\n</para>\n<para lang=\"en\">\nbackend SSL connections in the stream module\nmight hang after an SSL handshake.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nуровень безопасности, доступный в OpenSSL 1.1.0 и новее,\nне учитывался при загрузке сертификатов сервера,\nесли был задан через \"@SECLEVEL=N\" в директиве ssl_ciphers.\n</para>\n<para lang=\"en\">\nthe security level, which is available in OpenSSL 1.1.0 or newer,\ndid not affect loading of the server certificates\nwhen set with \"@SECLEVEL=N\" in the \"ssl_ciphers\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL-соединения с gRPC-бэкендами могли зависать,\nесли использовались методы select, poll или /dev/poll.\n</para>\n<para lang=\"en\">\nSSL connections with gRPC backends might hang\nif select, poll, or /dev/poll methods were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nтело запроса всегда записывалось на диск,\nесли в запросе не было строки заголовка \"Content-Length\".\n</para>\n<para lang=\"en\">\nwhen using HTTP/2\nclient request body was always written to disk\nif the \"Content-Length\" header line was not present in the request.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.21.1\" date=\"2021-07-06\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx для метода CONNECT всегда возвращает ошибку.\n</para>\n<para lang=\"en\">\nnow nginx always returns an error for the CONNECT method.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx всегда возвращает ошибку,\nесли в запросе одновременно присутствуют строки заголовка \"Content-Length\"\nи \"Transfer-Encoding\".\n</para>\n<para lang=\"en\">\nnow nginx always returns an error\nif both \"Content-Length\" and \"Transfer-Encoding\" header lines\nare present in the request.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx всегда возвращает ошибку,\nесли в строке запроса используются пробелы или управляющие символы.\n</para>\n<para lang=\"en\">\nnow nginx always returns an error\nif spaces or control characters are used in the request line.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx всегда возвращает ошибку,\nесли в имени заголовка используются пробелы или управляющие символы.\n</para>\n<para lang=\"en\">\nnow nginx always returns an error\nif spaces or control characters are used in a header name.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx всегда возвращает ошибку,\nесли в строке \"Host\" заголовка запроса\nиспользуются пробелы или управляющие символы.\n</para>\n<para lang=\"en\">\nnow nginx always returns an error\nif spaces or control characters\nare used in the \"Host\" request header line.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nоптимизация тестирования конфигурации\nпри использовании большого количества listen-сокетов.\n</para>\n<para lang=\"en\">\noptimization of configuration testing\nwhen using many listening sockets.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не экранировал\nсимволы \"\"\", \"&lt;\", \">\", \"\\\", \"^\", \"`\", \"{\", \"|\", и \"}\"\nпри проксировании с изменением URI запроса.\n</para>\n<para lang=\"en\">\nnginx did not escape\n\"\"\", \"&lt;\", \">\", \"\\\", \"^\", \"`\", \"{\", \"|\", and \"}\" characters\nwhen proxying with changed URI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL-переменные могли быть пустыми при записи в лог;\nошибка появилась в 1.19.5.\n</para>\n<para lang=\"en\">\nSSL variables might be empty when used in logs;\nthe bug had appeared in 1.19.5.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nkeepalive-соединения с gRPC-бэкендами могли не закрываться\nпосле получения GOAWAY-фрейма.\n</para>\n<para lang=\"en\">\nkeepalive connections with gRPC backends might not be closed\nafter receiving a GOAWAY frame.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nуменьшено потребление памяти для долгоживущих запросов\nпри проксировании с использованием более 64 буферов.\n</para>\n<para lang=\"en\">\nreduced memory consumption for long-lived requests\nwhen proxying with more than 64 buffers.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.21.0\" date=\"2021-05-25\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании директивы resolver\nво время обработки ответа DNS-сервера\nмогла происходить перезапись одного байта памяти,\nчто позволяло атакующему,\nимеющему возможность подделывать UDP-пакеты от DNS-сервера,\nвызвать падение рабочего процесса\nили, потенциально, выполнение произвольного кода (CVE-2021-23017).\n</para>\n<para lang=\"en\">\n1-byte memory overwrite might occur\nduring DNS server response processing\nif the \"resolver\" directive was used,\nallowing an attacker\nwho is able to forge UDP packets from the DNS server\nto cause worker process crash\nor, potentially, arbitrary code execution (CVE-2021-23017).\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_ssl_certificate, proxy_ssl_certificate_key,\ngrpc_ssl_certificate, grpc_ssl_certificate_key,\nuwsgi_ssl_certificate и uwsgi_ssl_certificate_key\nподдерживают переменные.\n</para>\n<para lang=\"en\">\nvariables support\nin the \"proxy_ssl_certificate\", \"proxy_ssl_certificate_key\"\n\"grpc_ssl_certificate\", \"grpc_ssl_certificate_key\",\n\"uwsgi_ssl_certificate\", and \"uwsgi_ssl_certificate_key\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива max_errors в почтовом прокси-сервере.\n</para>\n<para lang=\"en\">\nthe \"max_errors\" directive in the mail proxy module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпочтовый прокси-сервер поддерживает POP3 и IMAP pipelining.\n</para>\n<para lang=\"en\">\nthe mail proxy module supports POP3 and IMAP pipelining.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр fastopen директивы listen в модуле stream.<br/>\nСпасибо Anbang Wen.\n</para>\n<para lang=\"en\">\nthe \"fastopen\" parameter of the \"listen\" directive in the stream module.<br/>\nThanks to Anbang Wen.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nспециальные символы не экранировались\nпри автоматическом перенаправлении с добавлением завершающего слэша.\n</para>\n<para lang=\"en\">\nspecial characters were not escaped\nduring automatic redirect with appended trailing slash.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SMTP pipelining\nсоединения с клиентами в почтовом прокси-сервере\nмогли неожиданно закрываться.\n</para>\n<para lang=\"en\">\nconnections with clients in the mail proxy module\nmight be closed unexpectedly\nwhen using SMTP pipelining.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.10\" date=\"2021-04-13\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nв директиве keepalive_requests значение по умолчанию изменено на 1000.\n</para>\n<para lang=\"en\">\nthe default value of the \"keepalive_requests\" directive was changed to 1000.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива keepalive_time.\n</para>\n<para lang=\"en\">\nthe \"keepalive_time\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $connection_time.\n</para>\n<para lang=\"en\">\nthe $connection_time variable.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпри использовании zlib-ng\nв логах появлялись сообщения \"gzip filter failed to use preallocated memory\".\n</para>\n<para lang=\"en\">\n\"gzip filter failed to use preallocated memory\" alerts appeared in logs\nwhen using zlib-ng.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.9\" date=\"2021-03-30\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с почтовым прокси-сервером,\nно без модуля ngx_mail_ssl_module;\nошибка появилась в 1.19.8.\n</para>\n<para lang=\"en\">\nnginx could not be built with the mail proxy module,\nbut without the ngx_mail_ssl_module;\nthe bug had appeared in 1.19.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри работе с gRPC-бэкендами могли возникать ошибки\n\"upstream sent response body larger than indicated content length\";\nошибка появилась в 1.19.1.\n</para>\n<para lang=\"en\">\n\"upstream sent response body larger than indicated content length\"\nerrors might occur when working with gRPC backends;\nthe bug had appeared in 1.19.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли клиент закрывал соединение в момент отбрасывания тела запроса,\nnginx мог не закрыть соединение до истечения keepalive-таймаута.\n</para>\n<para lang=\"en\">\nnginx might not close a connection till keepalive timeout expiration\nif the connection was closed by the client while discarding the request body.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри ожидании задержки limit_req или auth_delay, а также при работе с бэкендами\nnginx мог не обнаружить, что соединение уже закрыто клиентом.\n</para>\n<para lang=\"en\">\nnginx might not detect that a connection was already closed by the client\nwhen waiting for auth_delay or limit_req delay, or when working with backends.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв методе обработки соединений eventport.\n</para>\n<para lang=\"en\">\nin the eventport method.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.8\" date=\"2021-03-09\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв директиве proxy_cookie_flags теперь\nфлаги можно задавать с помощью переменных.\n</para>\n<para lang=\"en\">\nflags in the \"proxy_cookie_flags\" directive\ncan now contain variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр proxy_protocol в директиве listen,\nдирективы proxy_protocol и set_real_ip_from\nв почтовом прокси-сервере.\n</para>\n<para lang=\"en\">\nthe \"proxy_protocol\" parameter of the \"listen\" directive,\nthe \"proxy_protocol\" and \"set_real_ip_from\" directives\nin mail proxy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nHTTP/2-соединения сразу закрывались\nпри использовании \"keepalive_timeout 0\";\nошибка появилась в 1.19.7.\n</para>\n<para lang=\"en\">\nHTTP/2 connections were immediately closed\nwhen using \"keepalive_timeout 0\";\nthe bug had appeared in 1.19.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнекоторые ошибки логгировались как неизвестные,\nесли nginx был собран с glibc 2.32.\n</para>\n<para lang=\"en\">\nsome errors were logged as unknown\nif nginx was built with glibc 2.32.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв методе обработки соединений eventport.\n</para>\n<para lang=\"en\">\nin the eventport method.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.7\" date=\"2021-02-16\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nобработка соединений в HTTP/2 была изменена\nи теперь более соответствует HTTP/1.x;\nдирективы http2_recv_timeout, http2_idle_timeout\nи http2_max_requests упразднены,\nвместо них следует использовать директивы\nkeepalive_timeout и keepalive_requests.\n</para>\n<para lang=\"en\">\nconnections handling in HTTP/2 has been changed\nto better match HTTP/1.x;\nthe \"http2_recv_timeout\", \"http2_idle_timeout\",\nand \"http2_max_requests\" directives have been removed,\nthe \"keepalive_timeout\" and \"keepalive_requests\" directives\nshould be used instead.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы http2_max_field_size и http2_max_header_size упразднены,\nвместо них следует использовать директиву large_client_header_buffers.\n</para>\n<para lang=\"en\">\nthe \"http2_max_field_size\" and \"http2_max_header_size\" directives\nhave been removed,\nthe \"large_client_header_buffers\" directive should be used instead.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при исчерпании свободных соединений\nnginx закрывает не только keepalive-соединения,\nно и соединения в lingering close.\n</para>\n<para lang=\"en\">\nnow, if free worker connections are exhausted,\nnginx starts closing not only keepalive connections,\nbut also connections in lingering close.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв логах могли появляться сообщения \"zero size buf in output\",\nесли бэкенд возвращал некорректный ответ\nпри небуферизированном проксировании;\nошибка появилась в 1.19.1.\n</para>\n<para lang=\"en\">\n\"zero size buf in output\" alerts might appear in logs\nif an upstream server returned an incorrect response\nduring unbuffered proxying;\nthe bug had appeared in 1.19.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы return\nвместе с image_filter или xslt_stylesheet\nHEAD-запросы обрабатывались некорректно.\n</para>\n<para lang=\"en\">\nHEAD requests were handled incorrectly\nif the \"return\" directive was used\nwith the \"image_filter\" or \"xslt_stylesheet\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве add_trailer.\n</para>\n<para lang=\"en\">\nin the \"add_trailer\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.6\" date=\"2020-12-15\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"no live upstreams\",\nесли server в блоке upstream был помечен как down.\n</para>\n<para lang=\"en\">\n\"no live upstreams\" errors\nif a \"server\" inside \"upstream\" block was marked as \"down\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTPS в рабочем процессе мог произойти segmentation fault;\nошибка появилась в 1.19.5.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process if HTTPS was used;\nthe bug had appeared in 1.19.5.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx возвращал ошибку 400 на запросы вида\n<nobr>\"GET http://example.com?args HTTP/1.0\"</nobr>.\n</para>\n<para lang=\"en\">\nnginx returned the 400 response on requests like\n<nobr>\"GET http://example.com?args HTTP/1.0\"</nobr>.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модулях ngx_http_flv_module и ngx_http_mp4_module.<br/>\nСпасибо Chris Newton.\n</para>\n<para lang=\"en\">\nin the ngx_http_flv_module and ngx_http_mp4_module.<br/>\nThanks to Chris Newton.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.5\" date=\"2020-11-24\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -e.\n</para>\n<para lang=\"en\">\nthe -e switch.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпри сборке дополнительных модулей\nтеперь можно указывать одни и те же исходные файлы в разных модулях.\n</para>\n<para lang=\"en\">\nthe same source files can now be specified in different modules\nwhile building addon modules.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL shutdown не работал\nпри закрытии соединений с ожиданием дополнительных данных (lingering close).\n</para>\n<para lang=\"en\">\nSSL shutdown did not work\nwhen lingering close was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри работе с gRPC-бэкендами\nмогли возникать ошибки \"upstream sent frame for closed stream\".\n</para>\n<para lang=\"en\">\n\"upstream sent frame for closed stream\" errors might occur\nwhen working with gRPC backends.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво внутреннем API для обработки тела запроса.\n</para>\n<para lang=\"en\">\nin request body filters internal API.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.4\" date=\"2020-10-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssl_conf_command, proxy_ssl_conf_command, grpc_ssl_conf_command\nи uwsgi_ssl_conf_command.\n</para>\n<para lang=\"en\">\nthe \"ssl_conf_command\", \"proxy_ssl_conf_command\", \"grpc_ssl_conf_command\",\nand \"uwsgi_ssl_conf_command\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_reject_handshake.\n</para>\n<para lang=\"en\">\nthe \"ssl_reject_handshake\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_smtp_auth в почтовом прокси-сервере.\n</para>\n<para lang=\"en\">\nthe \"proxy_smtp_auth\" directive in mail proxy.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.3\" date=\"2020-09-29\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_set_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_set_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_cookie_flags.\n</para>\n<para lang=\"en\">\nthe \"proxy_cookie_flags\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива userid_flags.\n</para>\n<para lang=\"en\">\nthe \"userid_flags\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрасширение управления кэшированием stale-if-error\nошибочно применялось, если бэкенд возвращал ответ\nс кодом 500, 502, 503, 504, 403, 404 или 429.\n</para>\n<para lang=\"en\">\nthe \"stale-if-error\" cache control extension\nwas erroneously applied if backend returned a response\nwith status code 500, 502, 503, 504, 403, 404, or 429.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовалось кэширование\nи бэкенд возвращал ответы с строкой заголовка Vary,\nв логах могли появляться сообщения \"[crit] cache file ... has too long header\".\n</para>\n<para lang=\"en\">\n\"[crit] cache file ... has too long header\" messages might appear in logs\nif caching was used\nand the backend returned responses with the \"Vary\" header line.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпри использовании OpenSSL 1.1.1\nв логах могли появляться сообщения \"[crit] SSL_write() failed\".\n</para>\n<para lang=\"en\">\n\"[crit] SSL_write() failed\" messages might appear in logs\nwhen using OpenSSL 1.1.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв логах могли появляться сообщения\n\"SSL_shutdown() failed (SSL: ... bad write retry)\";\nошибка появилась в 1.19.2.\n</para>\n<para lang=\"en\">\n\"SSL_shutdown() failed (SSL: ... bad write retry)\"\nmessages might appear in logs;\nthe bug had appeared in 1.19.2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nв рабочем процессе мог произойти segmentation fault,\nесли ошибки с кодом 400 с помощью директивы error_page\nперенаправлялись в проксируемый location.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using HTTP/2\nif errors with code 400 were redirected to a proxied location\nusing the \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов при использовании HTTP/2 и подзапросов в модуле njs.\n</para>\n<para lang=\"en\">\nsocket leak when using HTTP/2 and subrequests in the njs module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.2\" date=\"2020-08-11\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx начинает закрывать keepalive-соединения,\nне дожидаясь исчерпания всех свободных соединений,\nа также пишет об этом предупреждение в лог ошибок.\n</para>\n<para lang=\"en\">\nnow nginx starts closing keepalive connections\nbefore all free worker connections are exhausted,\nand logs a warning about this to the error log.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nоптимизация чтения тела запроса\nпри использовании chunked transfer encoding.\n</para>\n<para lang=\"en\">\noptimization of client request body reading\nwhen using chunked transfer encoding.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти при использовании директивы ssl_ocsp.\n</para>\n<para lang=\"en\">\nmemory leak if the \"ssl_ocsp\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв логах могли появляться сообщения \"zero size buf in output\",\nесли FastCGI-сервер возвращал некорректный ответ;\nошибка появилась в 1.19.1.\n</para>\n<para lang=\"en\">\n\"zero size buf in output\" alerts might appear in logs\nif a FastCGI server returned an incorrect response;\nthe bug had appeared in 1.19.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли размеры large_client_header_buffers отличались\nв разных виртуальных серверах.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif different large_client_header_buffers sizes were used\nin different virtual servers.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL shutdown мог не работать.\n</para>\n<para lang=\"en\">\nSSL shutdown might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв логах могли появляться сообщения\n\"SSL_shutdown() failed (SSL: ... bad write retry)\".\n</para>\n<para lang=\"en\">\n\"SSL_shutdown() failed (SSL: ... bad write retry)\"\nmessages might appear in logs.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_slice_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_slice_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_xslt_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_xslt_filter_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.1\" date=\"2020-07-07\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы lingering_close, lingering_time и lingering_timeout\nтеперь работают при использовании HTTP/2.\n</para>\n<para lang=\"en\">\nthe \"lingering_close\", \"lingering_time\", and \"lingering_timeout\" directives\nnow work when using HTTP/2.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь лишние данные, присланные бэкендом, всегда отбрасываются.\n</para>\n<para lang=\"en\">\nnow extra data sent by a backend are always discarded.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при получении слишком короткого ответа от FastCGI-сервера\nnginx пытается отправить клиенту доступную часть ответа,\nпосле чего закрывает соединение с клиентом.\n</para>\n<para lang=\"en\">\nnow after receiving a too short response from a FastCGI server\nnginx tries to send the available part of the response to the client,\nand then closes the client connection.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при получении ответа некорректной длины от gRPC-бэкенда\nnginx прекращает обработку ответа с ошибкой.\n</para>\n<para lang=\"en\">\nnow after receiving a response with incorrect length from a gRPC backend\nnginx stops response processing with an error.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр min_free в директивах proxy_cache_path, fastcgi_cache_path,\nscgi_cache_path и uwsgi_cache_path.<br/>\nСпасибо Adam Bambuch.\n</para>\n<para lang=\"en\">\nthe \"min_free\" parameter of the \"proxy_cache_path\", \"fastcgi_cache_path\",\n\"scgi_cache_path\", and \"uwsgi_cache_path\" directives.<br/>\nThanks to Adam Bambuch.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не удалял unix domain listen-сокеты\nпри плавном завершении по сигналу SIGQUIT.\n</para>\n<para lang=\"en\">\nnginx did not delete unix domain listen sockets\nduring graceful shutdown on the SIGQUIT signal.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nUDP-пакеты нулевого размера не проксировались.\n</para>\n<para lang=\"en\">\nzero length UDP datagrams were not proxied.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроксирование на uwsgi-бэкенды с использованием SSL могло не работать.<br/>\nСпасибо Guanzhong Chen.\n</para>\n<para lang=\"en\">\nproxying to uwsgi backends using SSL might not work.<br/>\nThanks to Guanzhong Chen.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок при использовании директивы ssl_ocsp.\n</para>\n<para lang=\"en\">\nin error handling when using the \"ssl_ocsp\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании файловых систем XFS и NFS\nразмер кэша на диске мог считаться некорректно.\n</para>\n<para lang=\"en\">\non XFS and NFS file systems\ndisk cache size might be calculated incorrectly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли сервер memcached возвращал некорректный ответ,\nв логах могли появляться сообщения \"negative size buf in writer\".\n</para>\n<para lang=\"en\">\n\"negative size buf in writer\" alerts might appear in logs\nif a memcached server returned a malformed response.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.19.0\" date=\"2020-05-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпроверка клиентских сертификатов с помощью OCSP.\n</para>\n<para lang=\"en\">\nclient certificate validation with OCSP.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри работе с gRPC-бэкендами\nмогли возникать ошибки \"upstream sent frame for closed stream\".\n</para>\n<para lang=\"en\">\n\"upstream sent frame for closed stream\" errors might occur\nwhen working with gRPC backends.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nOCSP stapling мог не работать,\nесли не была указана директива resolver.\n</para>\n<para lang=\"en\">\nOCSP stapling might not work\nif the \"resolver\" directive was not specified.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсоединения с некорректным HTTP/2 preface не логгировались.\n</para>\n<para lang=\"en\">\nconnections with incorrect HTTP/2 preface were not logged.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.10\" date=\"2020-04-14\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива auth_delay.\n</para>\n<para lang=\"en\">\nthe \"auth_delay\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.9\" date=\"2020-03-03\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx не разрешает\nнесколько строк \"Host\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnow nginx does not allow\nseveral \"Host\" request header lines.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx игнорировал дополнительные\nстроки \"Transfer-Encoding\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnginx ignored additional\n\"Transfer-Encoding\" request header lines.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов при использовании HTTP/2.\n</para>\n<para lang=\"en\">\nsocket leak when using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался OCSP stapling.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif OCSP stapling was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении ошибок с кодом 494 с помощью директивы error_page\nnginx возвращал ответ с кодом 494 вместо 400.\n</para>\n<para lang=\"en\">\nnginx used status code 494 instead of 400\nif errors with code 494 were redirected with the \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов при использовании подзапросов в модуле njs и директивы aio.\n</para>\n<para lang=\"en\">\nsocket leak when using subrequests in the njs module and the \"aio\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.8\" date=\"2020-01-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива grpc_pass поддерживает переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"grpc_pass\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри обработке pipelined-запросов по SSL-соединению мог произойти таймаут;\nошибка появилась в 1.17.5.\n</para>\n<para lang=\"en\">\na timeout might occur while handling pipelined requests in an SSL connection;\nthe bug had appeared in 1.17.5.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве debug_points при использовании HTTP/2.<br/>\nСпасибо Даниилу Бондареву.\n</para>\n<para lang=\"en\">\nin the \"debug_points\" directive when using HTTP/2.<br/>\nThanks to Daniil Bondarev.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.7\" date=\"2019-12-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна старте или во время переконфигурации мог произойти segmentation fault,\nесли в конфигурации использовалась\nдиректива rewrite с пустой строкой замены.\n</para>\n<para lang=\"en\">\na segmentation fault might occur on start or during reconfiguration\nif the \"rewrite\" directive with an empty replacement string\nwas used in the configuration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли директива break использовалась совместно с директивой alias\nили директивой proxy_pass с URI.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"break\" directive was used with the \"alias\" directive\nor with the \"proxy_pass\" directive with a URI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстрока Location заголовка ответа могла содержать мусор,\nесли URI запроса был изменён на URI, содержащий нулевой символ.\n</para>\n<para lang=\"en\">\nthe \"Location\" response header line might contain garbage\nif the request URI was rewritten to the one containing a null character.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри возврате перенаправлений с помощью директивы error_page\nзапросы с телом обрабатывались некорректно;\nошибка появилась в 0.7.12.\n</para>\n<para lang=\"en\">\nrequests with bodies were handled incorrectly\nwhen returning redirections with the \"error_page\" directive;\nthe bug had appeared in 0.7.12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов при использовании HTTP/2.\n</para>\n<para lang=\"en\">\nsocket leak when using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри обработке pipelined-запросов по SSL-соединению мог произойти таймаут;\nошибка появилась в 1.17.5.\n</para>\n<para lang=\"en\">\na timeout might occur while handling pipelined requests in an SSL connection;\nthe bug had appeared in 1.17.5.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_dav_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_dav_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.6\" date=\"2019-11-19\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $proxy_protocol_server_addr и $proxy_protocol_server_port.\n</para>\n<para lang=\"en\">\nthe $proxy_protocol_server_addr and $proxy_protocol_server_port variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива limit_conn_dry_run.\n</para>\n<para lang=\"en\">\nthe \"limit_conn_dry_run\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $limit_req_status и $limit_conn_status.\n</para>\n<para lang=\"en\">\nthe $limit_req_status and $limit_conn_status variables.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.5\" date=\"2019-10-22\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx использует вызов ioctl(FIONREAD), если он доступен,\nчтобы избежать чтения из быстрого соединения в течение долгого времени.\n</para>\n<para lang=\"en\">\nnow nginx uses ioctl(FIONREAD), if available,\nto avoid reading from a fast connection for a long time.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнеполные закодированные символы в конце URI запроса игнорировались.\n</para>\n<para lang=\"en\">\nincomplete escaped characters at the end of the request URI were ignored.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\n\"/.\" и \"/..\" в конце URI запроса не нормализовывались.\n</para>\n<para lang=\"en\">\n\"/.\" and \"/..\" at the end of the request URI were not normalized.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве merge_slashes.\n</para>\n<para lang=\"en\">\nin the \"merge_slashes\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве ignore_invalid_headers.<br/>\nСпасибо Alan Kemp.\n</para>\n<para lang=\"en\">\nin the \"ignore_invalid_headers\" directive.<br/>\nThanks to Alan Kemp.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с MinGW-w64 gcc 8.1 и новее.\n</para>\n<para lang=\"en\">\nnginx could not be built with MinGW-w64 gcc 8.1 or newer.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.4\" date=\"2019-09-24\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nулучшено детектирование некорректного поведения клиентов в HTTP/2.\n</para>\n<para lang=\"en\">\nbetter detection of incorrect client behavior in HTTP/2.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв обработке непрочитанного тела запроса\nпри возврате ошибок в HTTP/2.\n</para>\n<para lang=\"en\">\nin handling of not fully read client request body\nwhen returning errors in HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива worker_shutdown_timeout могла не работать\nпри использовании HTTP/2.\n</para>\n<para lang=\"en\">\nthe \"worker_shutdown_timeout\" directive might not work\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 и директивы proxy_request_buffering\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using HTTP/2 and the \"proxy_request_buffering\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна Windows при использовании SSL\nуровень записи в лог ошибки ECONNABORTED был \"crit\" вместо \"error\".\n</para>\n<para lang=\"en\">\nthe ECONNABORTED error log level was \"crit\" instead of \"error\"\non Windows when using SSL.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx игнорировал лишние данные при использовании chunked transfer encoding.\n</para>\n<para lang=\"en\">\nnginx ignored extra data when using chunked transfer encoding.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовалась директива return и\nпри чтении тела запроса возникала ошибка,\nnginx всегда возвращал ошибку 500.\n</para>\n<para lang=\"en\">\nnginx always returned the 500 error\nif the \"return\" directive was used\nand an error occurred during reading client request body.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти.\n</para>\n<para lang=\"en\">\nin memory allocation error handling.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.3\" date=\"2019-08-13\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании HTTP/2 клиент мог вызвать\nчрезмерное потребление памяти и ресурсов процессора\n(CVE-2019-9511, CVE-2019-9513, CVE-2019-9516).\n</para>\n<para lang=\"en\">\nwhen using HTTP/2 a client might cause\nexcessive memory consumption and CPU usage\n(CVE-2019-9511, CVE-2019-9513, CVE-2019-9516).\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании сжатия в логах могли появляться сообщения \"zero size buf\";\nошибка появилась в 1.17.2.\n</para>\n<para lang=\"en\">\n\"zero size buf\" alerts might appear in logs when using gzipping;\nthe bug had appeared in 1.17.2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы resolver в SMTP прокси-сервере\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"resolver\" directive was used in SMTP proxy.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.2\" date=\"2019-07-23\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nминимальная поддерживаемая версия zlib&mdash;1.2.0.4.<br/>\nСпасибо Илье Леошкевичу.\n</para>\n<para lang=\"en\">\nminimum supported zlib version is 1.2.0.4.<br/>\nThanks to Ilya Leoshkevich.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nметод $r->internal_redirect() встроенного перла\nтеперь ожидает закодированный URI.\n</para>\n<para lang=\"en\">\nthe $r->internal_redirect() embedded perl method\nnow expects escaped URIs.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь с помощью метода $r->internal_redirect() встроенного перла\nможно перейти в именованный location.\n</para>\n<para lang=\"en\">\nit is now possible to switch to a named location\nusing the $r->internal_redirect() embedded perl method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок во встроенном перле.\n</para>\n<para lang=\"en\">\nin error handling in embedded perl.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна старте или во время переконфигурации мог произойти segmentation fault,\nесли в конфигурации использовалось значение hash bucket size больше 64 килобайт.\n</para>\n<para lang=\"en\">\na segmentation fault might occur on start or during reconfiguration\nif hash bucket size larger than 64 kilobytes was used in the configuration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании методов обработки соединений select, poll и /dev/poll\nnginx мог нагружать процессор во время небуферизованного проксирования\nи при проксировании WebSocket-соединений.\n</para>\n<para lang=\"en\">\nnginx might hog CPU during unbuffered proxying\nand when proxying WebSocket connections\nif the select, poll, or /dev/poll methods were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_xslt_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_xslt_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_ssi_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_ssi_filter_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.1\" date=\"2019-06-25\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива limit_req_dry_run.\n</para>\n<para lang=\"en\">\nthe \"limit_req_dry_run\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпри использовании директивы hash в блоке upstream\nпустой ключ хэширования теперь приводит к переключению\nна round-robin балансировку.<br/>\nСпасибо Niklas Keller.\n</para>\n<para lang=\"en\">\nwhen using the \"hash\" directive inside the \"upstream\" block\nan empty hash key now triggers round-robin balancing.<br/>\nThanks to Niklas Keller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалось кэширование и директива image_filter,\nа ошибки с кодом 415 перенаправлялись с помощью директивы error_page;\nошибка появилась в 1.11.10.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif caching was used along with the \"image_filter\" directive,\nand errors with code 415 were redirected with the \"error_page\" directive;\nthe bug had appeared in 1.11.10.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался встроенный перл;\nошибка появилась в 1.7.3.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif embedded perl was used;\nthe bug had appeared in 1.7.3.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.17.0\" date=\"2019-05-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы limit_rate и limit_rate_after поддерживают переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"limit_rate\" and \"limit_rate_after\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_upload_rate и proxy_download_rate в модуле stream\nподдерживают переменные.\n</para>\n<para lang=\"en\">\nvariables support\nin the \"proxy_upload_rate\" and \"proxy_download_rate\" directives\nin the stream module.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nминимальная поддерживаемая версия OpenSSL&mdash;0.9.8.\n</para>\n<para lang=\"en\">\nminimum supported OpenSSL version is 0.9.8.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь postpone-фильтр собирается всегда.\n</para>\n<para lang=\"en\">\nnow the postpone filter is always built.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива include не работала в блоках if и limit_except.\n</para>\n<para lang=\"en\">\nthe \"include\" directive did not work inside the \"if\" and \"limit_except\" blocks.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке byte ranges.\n</para>\n<para lang=\"en\">\nin byte ranges processing.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.12\" date=\"2019-04-16\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли в директивах ssl_certificate или ssl_certificate_key\nиспользовались переменные\nи был включён OCSP stapling.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif variables were used\nin the \"ssl_certificate\" or \"ssl_certificate_key\" directives\nand OCSP stapling was enabled.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.11\" date=\"2019-04-09\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве ssl_stapling_file на Windows.\n</para>\n<para lang=\"en\">\nin the \"ssl_stapling_file\" directive on Windows.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.10\" date=\"2019-03-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при использовании имени хоста в директиве listen\nnginx создаёт listen-сокеты для всех адресов,\nсоответствующих этому имени\n(ранее использовался только первый адрес).\n</para>\n<para lang=\"en\">\nwhen using a hostname in the \"listen\" directive\nnginx now creates listening sockets\nfor all addresses the hostname resolves to\n(previously, only the first address was used).\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиапазоны портов в директиве listen.\n</para>\n<para lang=\"en\">\nport ranges in the \"listen\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nвозможность загрузки SSL-сертификатов и секретных ключей из переменных.\n</para>\n<para lang=\"en\">\nloading of SSL certificates and secret keys from variables.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпеременная $ssl_server_name могла быть пустой\nпри использовании OpenSSL 1.1.1.\n</para>\n<para lang=\"en\">\nthe $ssl_server_name variable might be empty\nwhen using OpenSSL 1.1.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не собирался с Visual Studio 2015 и новее;\nошибка появилась в 1.15.9.\n</para>\n<para lang=\"en\">\nnginx/Windows could not be built with Visual Studio 2015 or newer;\nthe bug had appeared in 1.15.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.9\" date=\"2019-02-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssl_certificate и ssl_certificate_key\nподдерживают переменные.\n</para>\n<para lang=\"en\">\nvariables support\nin the \"ssl_certificate\" and \"ssl_certificate_key\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nметод poll теперь доступен на Windows\nпри использовании Windows Vista и новее.\n</para>\n<para lang=\"en\">\nthe \"poll\" method is now available on Windows\nwhen using Windows Vista or newer.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли при использовании метода select на Windows\nпроисходила ошибка при установлении соединения с бэкендом,\nnginx ожидал истечения таймаута на установление соединения.\n</para>\n<para lang=\"en\">\nif the \"select\" method was used on Windows\nand an error occurred while establishing a backend connection,\nnginx waited for the connection establishment timeout to expire.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_upload_rate и proxy_download_rate\nв модуле stream\nработали некорректно при проксировании UDP-пакетов.\n</para>\n<para lang=\"en\">\nthe \"proxy_upload_rate\" and \"proxy_download_rate\" directives\nin the stream module\nworked incorrectly when proxying UDP datagrams.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.8\" date=\"2018-12-25\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $upstream_bytes_sent.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe $upstream_bytes_sent variable.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновые директивы в скриптах подсветки синтаксиса для vim.<br/>\nСпасибо Геннадию Махомеду.\n</para>\n<para lang=\"en\">\nnew directives in vim syntax highlighting scripts.<br/>\nThanks to Gena Makhomed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_cache_background_update.\n</para>\n<para lang=\"en\">\nin the \"proxy_cache_background_update\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве geo при использовании unix domain listen-сокетов.\n</para>\n<para lang=\"en\">\nin the \"geo\" directive when using unix domain listen sockets.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпри использовании директивы ssl_early_data с OpenSSL\nв логах могли появляться сообщения\n\"ignoring stale global SSL error ... bad length\".\n</para>\n<para lang=\"en\">\nthe \"ignoring stale global SSL error ... bad length\"\nalerts might appear in logs\nwhen using the \"ssl_early_data\" directive with OpenSSL.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв nginx/Windows.\n</para>\n<para lang=\"en\">\nin nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_autoindex_module на 32-битных платформах.\n</para>\n<para lang=\"en\">\nin the ngx_http_autoindex_module on 32-bit platforms.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.7\" date=\"2018-11-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_requests в модуле stream.\n</para>\n<para lang=\"en\">\nthe \"proxy_requests\" directive in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр \"delay\" директивы \"limit_req\".<br/>\nСпасибо Владиславу Шабанову и Петру Щучкину.\n</para>\n<para lang=\"en\">\nthe \"delay\" parameter of the \"limit_req\" directive.<br/>\nThanks to Vladislav Shabanov and Peter Shchuchkin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти в случае ошибок при переконфигурации.\n</para>\n<para lang=\"en\">\nmemory leak on errors during reconfiguration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв переменных $upstream_response_time, $upstream_connect_time и\n$upstream_header_time.\n</para>\n<para lang=\"en\">\nin the $upstream_response_time, $upstream_connect_time, and\n$upstream_header_time variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался модуль ngx_http_mp4_module на 32-битных платформах.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the ngx_http_mp4_module was used on 32-bit platforms.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.6\" date=\"2018-11-06\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании HTTP/2 клиент мог вызвать\nчрезмерное потреблению памяти (CVE-2018-16843)\nи ресурсов процессора (CVE-2018-16844).\n</para>\n<para lang=\"en\">\nwhen using HTTP/2 a client might cause\nexcessive memory consumption (CVE-2018-16843)\nand CPU usage (CVE-2018-16844).\n</para>\n</change>\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри обработке специально созданного mp4-файла модулем ngx_http_mp4_module\nсодержимое памяти рабочего процесса могло быть отправлено клиенту\n(CVE-2018-16845).\n</para>\n<para lang=\"en\">\nprocessing of a specially crafted mp4 file with the ngx_http_mp4_module\nmight result in worker process memory disclosure\n(CVE-2018-16845).\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_socket_keepalive, fastcgi_socket_keepalive,\ngrpc_socket_keepalive, memcached_socket_keepalive,\nscgi_socket_keepalive и uwsgi_socket_keepalive.\n</para>\n<para lang=\"en\">\nthe \"proxy_socket_keepalive\", \"fastcgi_socket_keepalive\",\n\"grpc_socket_keepalive\", \"memcached_socket_keepalive\",\n\"scgi_socket_keepalive\", and \"uwsgi_socket_keepalive\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx был собран с OpenSSL 1.1.0, а использовался с OpenSSL 1.1.1,\nпротокол TLS 1.3 всегда был разрешён.\n</para>\n<para lang=\"en\">\nif nginx was built with OpenSSL 1.1.0 and used with OpenSSL 1.1.1,\nthe TLS 1.3 protocol was always enabled.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри работе с gRPC-бэкендами могло расходоваться большое количество памяти.\n</para>\n<para lang=\"en\">\nworking with gRPC backends might result in excessive memory consumption.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.5\" date=\"2018-10-02\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании OpenSSL 1.1.0h и новее\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 1.15.4.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using OpenSSL 1.1.0h or newer;\nthe bug had appeared in 1.15.4.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнезначительных потенциальных ошибок.\n</para>\n<para lang=\"en\">\nof minor potential bugs.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.4\" date=\"2018-09-25\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директиву ssl_early_data можно использовать с OpenSSL.\n</para>\n<para lang=\"en\">\nnow the \"ssl_early_data\" directive can be used with OpenSSL.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_uwsgi_module.<br/>\nСпасибо Chris Caputo.\n</para>\n<para lang=\"en\">\nin the ngx_http_uwsgi_module.<br/>\nThanks to Chris Caputo.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсоединения к некоторым gRPC-бэкендам могли не кэшироваться\nпри использовании директивы keepalive.\n</para>\n<para lang=\"en\">\nconnections with some gRPC backends might not be cached\nwhen using the \"keepalive\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы error_page для перенаправления ошибок,\nвозникающих на ранних этапах обработки запроса,\nв частности ошибок с кодом 400,\nмогла происходить утечка сокетов.\n</para>\n<para lang=\"en\">\na socket leak might occur\nwhen using the \"error_page\" directive\nto redirect early request processing errors,\nnotably errors with code 400.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива return при возврате ошибок не изменяла код ответа,\nесли запрос был перенаправлен с помощью директивы error_page.\n</para>\n<para lang=\"en\">\nthe \"return\" directive did not change the response code when returning errors\nif the request was redirected by the \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстандартные сообщения об ошибках и ответы модуля ngx_http_autoindex_module\nсодержали атрибут bgcolor, что могло приводить к их некорректному отображению\nпри использовании пользовательских настроек цветов в браузерах.<br/>\nСпасибо Nova DasSarma.\n</para>\n<para lang=\"en\">\nstandard error pages and responses of the ngx_http_autoindex_module module\nused the \"bgcolor\" attribute, and might be displayed incorrectly when using\ncustom color settings in browsers.<br/>\nThanks to Nova DasSarma.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nуровень логгирования ошибок SSL \"no suitable key share\" и\n\"no suitable signature algorithm\"\nпонижен с уровня crit до info.\n</para>\n<para lang=\"en\">\nthe logging level of the \"no suitable key share\" and\n\"no suitable signature algorithm\" SSL errors\nhas been lowered from \"crit\" to \"info\".\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.3\" date=\"2018-08-28\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь TLSv1.3 можно использовать с BoringSSL.\n</para>\n<para lang=\"en\">\nnow TLSv1.3 can be used with BoringSSL.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_early_data,\nсейчас доступна при использовании BoringSSL.\n</para>\n<para lang=\"en\">\nthe \"ssl_early_data\" directive,\ncurrently available with BoringSSL.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы keepalive_timeout и keepalive_requests\nв блоке upstream.\n</para>\n<para lang=\"en\">\nthe \"keepalive_timeout\" and \"keepalive_requests\" directives\nin the \"upstream\" block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module\nпри копировании файла поверх существующего файла с помощью метода COPY\nне обнулял целевой файл.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module\ndid not truncate destination file when copying a file over an existing one\nwith the COPY method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module\nпри перемещении файла между файловыми системами с помощью метода MOVE\nустанавливал нулевые права доступа на результирующий файл\nи не сохранял время изменения файла.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module\nused zero access rights on the destination file\nand did not preserve file modification time\nwhen moving a file between different file systems with the MOVE method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module\nпри копировании файла с помощью метода COPY\nдля результирующего файла использовал права доступа по умолчанию.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module\nused default access rights\nwhen copying a file with the COPY method.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nнекоторые клиенты могли не работать при использовании HTTP/2;\nошибка появилась в 1.13.5.\n</para>\n<para lang=\"en\">\nsome clients might not work when using HTTP/2;\nthe bug had appeared in 1.13.5.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с LibreSSL 2.8.0.\n</para>\n<para lang=\"en\">\nnginx could not be built with LibreSSL 2.8.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.2\" date=\"2018-07-24\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_preread_protocol\nв модуле ngx_stream_ssl_preread_module.\n</para>\n<para lang=\"en\">\nthe $ssl_preread_protocol variable\nin the ngx_stream_ssl_preread_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при использовании директивы reset_timedout_connection\nnginx сбрасывает соединения, закрываемые с кодом 444.\n</para>\n<para lang=\"en\">\nnow when using the \"reset_timedout_connection\" directive\nnginx will reset connections being closed with the 444 code.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nуровень логгирования ошибок SSL \"http request\", \"https proxy request\",\n\"unsupported protocol\" и \"version too low\"\nпонижен с уровня crit до info.\n</para>\n<para lang=\"en\">\na logging level of the \"http request\", \"https proxy request\",\n\"unsupported protocol\", and \"version too low\" SSL errors\nhas been lowered from \"crit\" to \"info\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзапросы к DNS-серверу не отправлялись повторно,\nесли при первой попытке отправки происходила ошибка.\n</para>\n<para lang=\"en\">\nDNS requests were not resent\nif initial sending of a request failed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр reuseport директивы listen игнорировался,\nесли количество рабочих процессов было задано после директивы listen.\n</para>\n<para lang=\"en\">\nthe \"reuseport\" parameter of the \"listen\" directive was ignored\nif the number of worker processes was specified after the \"listen\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании OpenSSL 1.1.0 и новее\nдирективу ssl_prefer_server_ciphers нельзя было выключить\nв виртуальном сервере, если она была включена в сервере по умолчанию.\n</para>\n<para lang=\"en\">\nwhen using OpenSSL 1.1.0 or newer\nit was not possible to switch off \"ssl_prefer_server_ciphers\" in\na virtual server if it was switched on in the default server.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nповторное использование SSL-сессий к бэкендам\nне работало с протоколом TLS 1.3.\n</para>\n<para lang=\"en\">\nSSL session reuse with upstream servers\ndid not work with the TLS 1.3 protocol.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.1\" date=\"2018-07-03\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива random в блоке upstream.\n</para>\n<para lang=\"en\">\nthe \"random\" directive inside the \"upstream\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nулучшена производительность при использовании директив hash и ip_hash\nсовместно с директивой zone.\n</para>\n<para lang=\"en\">\nimproved performance when using the \"hash\" and \"ip_hash\" directives\nwith the \"zone\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр reuseport директивы listen\nтеперь использует SO_REUSEPORT_LB на FreeBSD 12.\n</para>\n<para lang=\"en\">\nthe \"reuseport\" parameter of the \"listen\" directive\nnow uses SO_REUSEPORT_LB on FreeBSD 12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nHTTP/2 server push не работал, если SSL терминировался прокси-сервером\nперед nginx'ом.\n</para>\n<para lang=\"en\">\nHTTP/2 server push did not work if SSL was terminated by a proxy server\nin front of nginx.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива tcp_nopush всегда использовалась для соединений к бэкендам.\n</para>\n<para lang=\"en\">\nthe \"tcp_nopush\" directive was always used on backend connections.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри отправке сохранённого на диск тела запроса на gRPC-бэкенд\nмогли возникать ошибки.\n</para>\n<para lang=\"en\">\nsending a disk-buffered request body to a gRPC backend\nmight fail.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.15.0\" date=\"2018-06-05\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива \"ssl\" теперь считается устаревшей;\nвместо неё следует использовать параметр ssl директивы listen.\n</para>\n<para lang=\"en\">\nthe \"ssl\" directive is deprecated;\nthe \"ssl\" parameter of the \"listen\" directive should be used instead.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при использовании директивы listen с параметром ssl\nnginx определяет отсутствие SSL-сертификатов при тестировании конфигурации.\n</para>\n<para lang=\"en\">\nnow nginx detects missing SSL certificates during configuration testing\nwhen using the \"ssl\" parameter of the \"listen\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь модуль stream умеет обрабатывать\nнесколько входящих UDP-пакетов от клиента в рамках одной сессии.\n</para>\n<para lang=\"en\">\nnow the stream module can handle\nmultiple incoming UDP datagrams from a client within a single session.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_cache_valid\nможно было указать некорректный код ответа.\n</para>\n<para lang=\"en\">\nit was possible to specify an incorrect response code\nin the \"proxy_cache_valid\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался gcc 8.1.\n</para>\n<para lang=\"en\">\nnginx could not be built by gcc 8.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nлоггирование в syslog останавливалось при изменении локального IP-адреса.\n</para>\n<para lang=\"en\">\nlogging to syslog stopped on local IP address changes.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался компилятором clang, если был установлен CUDA SDK;\nошибка появилась в 1.13.8.\n</para>\n<para lang=\"en\">\nnginx could not be built by clang with CUDA SDK installed;\nthe bug had appeared in 1.13.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании unix domain listen-сокетов на FreeBSD\nв процессе обновления исполняемого файла\nв логе могли появляться сообщения \"getsockopt(TCP_FASTOPEN) ... failed\".\n</para>\n<para lang=\"en\">\n\"getsockopt(TCP_FASTOPEN) ... failed\" messages might appear in logs\nduring binary upgrade\nwhen using unix domain listen sockets on FreeBSD.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Fedora 28 Linux.\n</para>\n<para lang=\"en\">\nnginx could not be built on Fedora 28 Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы limit_req\nзаданная скорость обработки запросов могла не соблюдаться.\n</para>\n<para lang=\"en\">\nrequest processing rate might exceed configured rate\nwhen using the \"limit_req\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке адресов клиентов при использовании unix domain listen-сокетов\nдля работы с датаграммами на Linux.\n</para>\n<para lang=\"en\">\nin handling of client addresses when using unix domain listen sockets\nto work with datagrams on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти.\n</para>\n<para lang=\"en\">\nin memory allocation error handling.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.12\" date=\"2018-04-10\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри возврате большого ответа\nсоединения с gRPC-бэкендами могли неожиданно закрываться.\n</para>\n<para lang=\"en\">\nconnections with gRPC backends might be closed unexpectedly\nwhen returning a large response.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.11\" date=\"2018-04-03\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр proxy_protocol директивы listen\nтеперь поддерживает протокол PROXY версии 2.\n</para>\n<para lang=\"en\">\nthe \"proxy_protocol\" parameter of the \"listen\" directive\nnow supports the PROXY protocol version 2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с OpenSSL 1.1.1 статически на Linux.\n</para>\n<para lang=\"en\">\nnginx could not be built with OpenSSL 1.1.1 statically on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв параметрах http_404, http_500 и им подобных\nдирективы proxy_next_upstream.\n</para>\n<para lang=\"en\">\nin the \"http_404\", \"http_500\", etc. parameters\nof the \"proxy_next_upstream\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.10\" date=\"2018-03-20\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь параметр set в SSI-директиве include\nпозволяет сохранять в переменную любые ответы;\nмаксимальный размер ответа задаётся директивой subrequest_output_buffer_size.\n</para>\n<para lang=\"en\">\nthe \"set\" parameter of the \"include\" SSI directive now allows\nwriting arbitrary responses to a variable;\nthe \"subrequest_output_buffer_size\" directive defines maximum response size.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx использует вызов clock_gettime(CLOCK_MONOTONIC), если он доступен,\nчто позволяет избежать некорректного срабатывания таймаутов\nпри изменениях системного времени.\n</para>\n<para lang=\"en\">\nnow nginx uses clock_gettime(CLOCK_MONOTONIC) if available,\nto avoid timeouts being incorrectly triggered\non system time changes.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр \"escape=none\" директивы log_format.<br/>\nСпасибо Johannes Baiter и Calin Don.\n</para>\n<para lang=\"en\">\nthe \"escape=none\" parameter of the \"log_format\" directive.<br/>\nThanks to Johannes Baiter and Calin Don.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_preread_alpn_protocols\nв модуле ngx_stream_ssl_preread_module.\n</para>\n<para lang=\"en\">\nthe $ssl_preread_alpn_protocols variable\nin the ngx_stream_ssl_preread_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_grpc_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_grpc_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти в директиве geo.\n</para>\n<para lang=\"en\">\nin memory allocation error handling in the \"geo\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменных в директиве auth_basic_user_file\nв лог мог выводиться символ '\\0'.<br/>\nСпасибо Вадиму Филимонову.\n</para>\n<para lang=\"en\">\nwhen using variables in the \"auth_basic_user_file\" directive\na null character might appear in logs.<br/>\nThanks to Vadim Filimonov.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.9\" date=\"2018-02-20\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка HTTP/2 server push;\nдирективы http2_push и http2_push_preload.\n</para>\n<para lang=\"en\">\nHTTP/2 server push support;\nthe \"http2_push\" and \"http2_push_preload\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании кэша\nв логах могли появляться сообщения \"header already sent\";\nошибка появилась в 1.9.13.\n</para>\n<para lang=\"en\">\n\"header already sent\" alerts might appear in logs\nwhen using cache;\nthe bug had appeared in 1.9.13.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы ssl_verify_client\nв рабочем процессе мог произойти segmentation fault,\nесли в виртуальном сервере не был указан SSL-сертификат.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"ssl_verify_client\" directive was used\nand no SSL certificate was specified in a virtual server.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_v2_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_v2_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_dav_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_dav_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.8\" date=\"2017-12-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при использовании параметра transparent директив proxy_bind,\nfastcgi_bind, memcached_bind, scgi_bind и uwsgi_bind\nnginx автоматически сохраняет capability CAP_NET_RAW в рабочих процессах.\n</para>\n<para lang=\"en\">\nnow nginx automatically preserves the CAP_NET_RAW capability in worker processes\nwhen using the \"transparent\" parameter of the \"proxy_bind\",\n\"fastcgi_bind\", \"memcached_bind\", \"scgi_bind\", and \"uwsgi_bind\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nулучшения в определении размера строки кэша процессора.<br/>\nСпасибо Debayan Ghosh.\n</para>\n<para lang=\"en\">\nimproved CPU cache line size detection.<br/>\nThanks to Debayan Ghosh.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновые директивы в скриптах подсветки синтаксиса для vim.<br/>\nСпасибо Геннадию Махомеду.\n</para>\n<para lang=\"en\">\nnew directives in vim syntax highlighting scripts.<br/>\nThanks to Gena Makhomed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроцедура обновления исполняемого файла не работала,\nесли после завершения родительского процесса\nновым родительским процессом nginx'а становился процесс с PID, отличным от 1.\n</para>\n<para lang=\"en\">\nbinary upgrade refused to work\nif nginx was re-parented to a process with PID different from 1\nafter its parent process has finished.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module неправильно обрабатывал запросы с телом.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module incorrectly handled requests with bodies.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_limit_rate при использовании с директивой keepalive.\n</para>\n<para lang=\"en\">\nin the \"proxy_limit_rate\" directive when used with the \"keepalive\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании \"proxy_buffering off\" часть ответа могла буферизироваться,\nесли клиентское соединение использовало SSL.<br/>\nСпасибо Patryk Lesiewicz.\n</para>\n<para lang=\"en\">\nsome parts of a response might be buffered when using \"proxy_buffering off\"\nif the client connection used SSL.<br/>\nThanks to Patryk Lesiewicz.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_cache_background_update.\n</para>\n<para lang=\"en\">\nin the \"proxy_cache_background_update\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременную вида \"${name}\" с именем в фигурных скобках\nнельзя было использовать в начале параметра\nне заключив весь параметр в кавычки.\n</para>\n<para lang=\"en\">\nit was not possible to start a parameter\nwith a variable in the \"${name}\" form with the name in curly brackets\nwithout enclosing the parameter into single or double quotes.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.7\" date=\"2017-11-21\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв переменной $upstream_status.\n</para>\n<para lang=\"en\">\nin the $upstream_status variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли бэкенд возвращал ответ \"101 Switching Protocols\" на подзапрос.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif a backend returned a \"101 Switching Protocols\" response to a subrequest.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли при переконфигурации изменялся размер зоны разделяемой памяти\nи переконфигурация завершалась неудачно,\nто в главном процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in a master process\nif a shared memory zone size was changed during a reconfiguration\nand the reconfiguration failed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_fastcgi_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_fastcgi_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx возвращал ошибку 500,\nесли в директиве xslt_stylesheet\nбыли заданы параметры без использования переменных.\n</para>\n<para lang=\"en\">\nnginx returned the 500 error\nif parameters without variables were specified\nin the \"xslt_stylesheet\" directive.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпри использовании варианта библиотеки zlib от Intel\nв лог писались сообщения \"gzip filter failed to use preallocated memory\".\n</para>\n<para lang=\"en\">\n\"gzip filter failed to use preallocated memory\" alerts appeared in logs\nwhen using a zlib library variant from Intel.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива worker_shutdown_timeout не работала\nпри использовании почтового прокси-сервера\nи при проксировании WebSocket-соединений.\n</para>\n<para lang=\"en\">\nthe \"worker_shutdown_timeout\" directive did not work\nwhen using mail proxy and when proxying WebSocket connections.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.6\" date=\"2017-10-10\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы ssl_preread\nв модуле stream не работало переключение на следующий бэкенд.\n</para>\n<para lang=\"en\">\nswitching to the next upstream server in the stream module did not work\nwhen using the \"ssl_preread\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_v2_module.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the ngx_http_v2_module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не поддерживал даты после 2038 года\nна 32-битных платформах с 64-битным time_t.\n</para>\n<para lang=\"en\">\nnginx did not support dates after the year 2038\non 32-bit platforms with 64-bit time_t.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке дат до 1970 года и после 10000 года.\n</para>\n<para lang=\"en\">\nin handling of dates prior to the year 1970 and after the year 10000.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле stream таймауты ожидания UDP-пакетов от бэкендов\nне логгировались или логгировались на уровне info вместо error.\n</para>\n<para lang=\"en\">\nin the stream module timeouts waiting for UDP datagrams from upstream servers\nwere not logged or logged at the \"info\" level instead of \"error\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 nginx мог вернуть ошибку 400,\nне указав в логе причину.\n</para>\n<para lang=\"en\">\nwhen using HTTP/2 nginx might return the 400 response\nwithout logging the reason.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке повреждённых файлов кэша.\n</para>\n<para lang=\"en\">\nin processing of corrupted cache files.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри кэшировании ошибок, перехваченных error_page,\nне учитывались заголовки управления кэшированием.\n</para>\n<para lang=\"en\">\ncache control headers were ignored\nwhen caching errors intercepted by error_page.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 тело запроса могло быть повреждено.\n</para>\n<para lang=\"en\">\nwhen using HTTP/2 client request body might be corrupted.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке адресов клиентов при использовании unix domain сокетов.\n</para>\n<para lang=\"en\">\nin handling of client addresses when using unix domain sockets.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы \"hash ... consistent\" в блоке upstream\nnginx нагружал процессор, если использовались большие веса\nи все или почти все бэкенды были недоступны.\n</para>\n<para lang=\"en\">\nnginx hogged CPU\nwhen using the \"hash ... consistent\" directive in the upstream block\nif large weights were used and all or most of the servers were unavailable.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.5\" date=\"2017-09-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_client_escaped_cert.\n</para>\n<para lang=\"en\">\nthe $ssl_client_escaped_cert variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива ssl_session_ticket_key и параметр include директивы geo\nне работали на Windows.\n</para>\n<para lang=\"en\">\nthe \"ssl_session_ticket_key\" directive and\nthe \"include\" parameter of the \"geo\" directive did not work on Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна 32-битных платформах\nпри запросе более 4 гигабайт с помощью нескольких диапазонов\nвозвращалась некорректная длина ответа.\n</para>\n<para lang=\"en\">\nincorrect response length was returned\non 32-bit platforms when requesting more than 4 gigabytes\nwith multiple ranges.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"expires modified\" и\nобработка строки If-Range заголовка запроса\nне учитывали время последнего изменения ответа,\nесли использовалось проксирование без кэширования.\n</para>\n<para lang=\"en\">\nthe \"expires modified\" directive and\nprocessing of the \"If-Range\" request header line\ndid not use the response last modification time\nif proxying without caching was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.4\" date=\"2017-08-08\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_mirror_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_mirror_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nклиентские соединения могли сбрасываться при тестировании конфигурации,\nесли использовался параметр reuseport директивы listen на Linux.\n</para>\n<para lang=\"en\">\nclient connections might be dropped during configuration testing\nwhen using the \"reuseport\" parameter of the \"listen\" directive on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтело запроса могло быть недоступно в подзапросах,\nесли оно было сохранено в файл и использовалось проксирование.\n</para>\n<para lang=\"en\">\nrequest body might not be available in subrequests\nif it was saved to a file and proxying was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nочистка кэша по max_size не работала на Windows.\n</para>\n<para lang=\"en\">\ncleaning cache based on the \"max_size\" parameter did not work on Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nлюбое выделение разделяемой памяти на Windows требовало 4096 байт памяти.\n</para>\n<para lang=\"en\">\nany shared memory allocation required 4096 bytes on Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы zone в блоке upstream на Windows\nрабочий процесс мог завершаться аварийно.\n</para>\n<para lang=\"en\">\nnginx worker might be terminated abnormally\nwhen using the \"zone\" directive inside the \"upstream\" block on Windows.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.3\" date=\"2017-07-11\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nспециально созданный запрос мог вызвать целочисленное переполнение\nв range-фильтре и последующую некорректную обработку запрошенных диапазонов,\nчто потенциально могло привести к утечке конфиденциальной информации\n(CVE-2017-7529).\n</para>\n<para lang=\"en\">\na specially crafted request might result in an integer overflow\nand incorrect processing of ranges in the range filter,\npotentially resulting in sensitive information leak\n(CVE-2017-7529).\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.2\" date=\"2017-06-27\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при запросе диапазона, начинающегося с 0, из пустого файла\nnginx возвращает ответ 200 вместо 416.\n</para>\n<para lang=\"en\">\nnginx now returns 200 instead of 416\nwhen a range starting with 0 is requested from an empty file.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива add_trailer.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe \"add_trailer\" directive.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под Cygwin и NetBSD;\nошибка появилась в 1.13.0.\n</para>\n<para lang=\"en\">\nnginx could not be built on Cygwin and NetBSD;\nthe bug had appeared in 1.13.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под MSYS2 / MinGW 64-bit.<br/>\nСпасибо Orgad Shaneh.\n</para>\n<para lang=\"en\">\nnginx could not be built under MSYS2 / MinGW 64-bit.<br/>\nThanks to Orgad Shaneh.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SSI с большим количеством подзапросов\nи proxy_pass с переменными\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using SSI with many includes\nand proxy_pass with variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_v2_module.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the ngx_http_v2_module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.1\" date=\"2017-05-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь в качестве параметра директивы set_real_ip_from\nможно указывать имя хоста.\n</para>\n<para lang=\"en\">\nnow a hostname can be used\nas the \"set_real_ip_from\" directive parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nулучшения в скриптах подсветки синтаксиса для vim.\n</para>\n<para lang=\"en\">\nvim syntax highlighting scripts improvements.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_cpu_affinity теперь работает на DragonFly BSD.<br/>\nСпасибо Sepherosa Ziehau.\n</para>\n<para lang=\"en\">\nthe \"worker_cpu_affinity\" directive now works on DragonFly BSD.<br/>\nThanks to Sepherosa Ziehau.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL renegotiation в соединениях к бэкендам\nне работал при использовании OpenSSL до 1.1.0.\n</para>\n<para lang=\"en\">\nSSL renegotiation on backend connections\ndid not work when using OpenSSL before 1.1.0.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nnginx не собирался с Oracle Developer Studio 12.5.\n</para>\n<para lang=\"en\">\nnginx could not be built with Oracle Developer Studio 12.5.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nтеперь cache manager пропускает заблокированные записи\nпри очистке кэша по max_size.\n</para>\n<para lang=\"en\">\nnow cache manager ignores long locked cache entries\nwhen cleaning cache based on the \"max_size\" parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nклиентские SSL-соединения сразу закрывались, если использовался\nотложенный accept и параметр proxy_protocol директивы listen.\n</para>\n<para lang=\"en\">\nclient SSL connections were immediately closed if deferred accept\nand the \"proxy_protocol\" parameter of the \"listen\" directive were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_cache_background_update.\n</para>\n<para lang=\"en\">\nin the \"proxy_cache_background_update\" directive.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nтеперь директива tcp_nodelay\nустанавливает опцию TCP_NODELAY перед SSL handshake.\n</para>\n<para lang=\"en\">\nnow the \"tcp_nodelay\" directive\nsets the TCP_NODELAY option before an SSL handshake.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.13.0\" date=\"2017-04-25\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь SSL renegotiation допускается в соединениях к бэкендам.\n</para>\n<para lang=\"en\">\nSSL renegotiation is now allowed on backend connections.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры rcvbuf и sndbuf директив listen\nв почтовом прокси-сервере и модуле stream.\n</para>\n<para lang=\"en\">\nthe \"rcvbuf\" and \"sndbuf\" parameters of the \"listen\" directives\nof the mail proxy and stream modules.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы return и error_page теперь могут использоваться для возврата\nперенаправлений с кодом 308.<br/>\nСпасибо Simon Leblanc.\n</para>\n<para lang=\"en\">\nthe \"return\" and \"error_page\" directives can now be used to return 308\nredirections.<br/>\nThanks to Simon Leblanc.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр TLSv1.3 в директиве ssl_protocols.\n</para>\n<para lang=\"en\">\nthe \"TLSv1.3\" parameter of the \"ssl_protocols\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпри логгировании сигналов теперь указывается PID отправившего сигнал процесса.\n</para>\n<para lang=\"en\">\nwhen logging signals nginx now logs PID of the process which sent the signal.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти.\n</para>\n<para lang=\"en\">\nin memory allocation error handling.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли сервер в модуле stream слушал на wildcard-адресе,\nисходящий адрес ответного UDP-пакета\nмог отличаться от адреса назначения исходного пакета.\n</para>\n<para lang=\"en\">\nif a server in the stream module listened on a wildcard address,\nthe source address of a response UDP datagram could differ\nfrom the original datagram destination address.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.13\" date=\"2017-04-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр http_429 в директивах proxy_next_upstream, fastcgi_next_upstream,\nscgi_next_upstream и uwsgi_next_upstream.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe \"http_429\" parameter of the \"proxy_next_upstream\", \"fastcgi_next_upstream\",\n\"scgi_next_upstream\", and \"uwsgi_next_upstream\" directives.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти.\n</para>\n<para lang=\"en\">\nin memory allocation error handling.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директив sendfile и timer_resolution на Linux\nзапросы могли зависать.\n</para>\n<para lang=\"en\">\nrequests might hang\nwhen using the \"sendfile\" and \"timer_resolution\" directives on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании с подзапросами директив sendfile и aio_write\nзапросы могли зависать.\n</para>\n<para lang=\"en\">\nrequests might hang\nwhen using the \"sendfile\" and \"aio_write\" directives with subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_v2_module.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the ngx_http_v2_module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 в рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process when using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзапросы могли зависать\nпри использовании с подзапросами директив limit_rate, sendfile_max_chunk,\nlimit_req или метода $r->sleep() встроенного перла.\n</para>\n<para lang=\"en\">\nrequests might hang\nwhen using the \"limit_rate\", \"sendfile_max_chunk\", \"limit_req\" directives,\nor the $r->sleep() embedded perl method with subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_slice_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_slice_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.12\" date=\"2017-03-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог нагружать процессор;\nошибка появилась в 1.11.11.\n</para>\n<para lang=\"en\">\nnginx might hog CPU;\nthe bug had appeared in 1.11.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.11\" date=\"2017-03-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_shutdown_timeout.\n</para>\n<para lang=\"en\">\nthe \"worker_shutdown_timeout\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nулучшения в скриптах подсветки синтаксиса для vim.<br/>\nСпасибо Wei-Ko Kao.\n</para>\n<para lang=\"en\">\nvim syntax highlighting scripts improvements.<br/>\nThanks to Wei-Ko Kao.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри попытке установить переменную $limit_rate в пустую строку\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the $limit_rate variable was set to an empty string.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_cache_background_update, fastcgi_cache_background_update,\nscgi_cache_background_update и uwsgi_cache_background_update\nмогли работать некорректно, если использовалась директива if.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_background_update\", \"fastcgi_cache_background_update\",\n\"scgi_cache_background_update\", and \"uwsgi_cache_background_update\" directives\nmight work incorrectly if the \"if\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли количество large_client_header_buffers в виртуальном сервере\nотличалось от такового в сервере по умолчанию.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif number of large_client_header_buffers in a virtual server\nwas different from the one in the default server.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв почтовом прокси-сервере.\n</para>\n<para lang=\"en\">\nin the mail proxy server.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.10\" date=\"2017-02-14\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nформат заголовка кэша был изменен,\nранее закэшированные ответы будут загружены заново.\n</para>\n<para lang=\"en\">\ncache header format has been changed,\npreviously cached responses will be invalidated.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка расширений stale-while-revalidate и stale-if-error\nв строке \"Cache-Control\" в заголовке ответа бэкенда.\n</para>\n<para lang=\"en\">\nsupport of \"stale-while-revalidate\" and \"stale-if-error\" extensions\nin the \"Cache-Control\" backend response header line.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_cache_background_update, fastcgi_cache_background_update,\nscgi_cache_background_update и uwsgi_cache_background_update.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_background_update\", \"fastcgi_cache_background_update\",\n\"scgi_cache_background_update\", and \"uwsgi_cache_background_update\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx может кэшировать ответы\nсо строкой Vary заголовка длиной до 128 символов\n(вместо 42 символов в предыдущих версиях).\n</para>\n<para lang=\"en\">\nnginx is now able to cache responses\nwith the \"Vary\" header line up to 128 characters long\n(instead of 42 characters in previous versions).\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр build директивы server_tokens.<br/>\nСпасибо Tom Thorogood.\n</para>\n<para lang=\"en\">\nthe \"build\" parameter of the \"server_tokens\" directive.<br/>\nThanks to Tom Thorogood.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри обработке запросов со строкой \"Expect: 100-continue\" в заголовке запроса\nв логах могли появляться сообщения \"[crit] SSL_write() failed\".\n</para>\n<para lang=\"en\">\n\"[crit] SSL_write() failed\" messages might appear in logs\nwhen handling requests with the \"Expect: 100-continue\" request header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_slice_module не работал в именованных location'ах.\n</para>\n<para lang=\"en\">\nthe ngx_http_slice_module did not work in named locations.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании AIO после перенаправления запроса с помощью X-Accel-Redirect\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using AIO after an \"X-Accel-Redirect\" redirection.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nуменьшено потребление памяти для долгоживущих запросов, использующих сжатие.\n</para>\n<para lang=\"en\">\nreduced memory consumption for long-lived requests using gzipping.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.9\" date=\"2017-01-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании модуля stream nginx мог нагружать процессор;\nошибка появилась в 1.11.5.\n</para>\n<para lang=\"en\">\nnginx might hog CPU when using the stream module;\nthe bug had appeared in 1.11.5.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод аутентификации EXTERNAL в почтовом прокси-сервере\nможно было использовать, даже если он не был разрешён в конфигурации.\n</para>\n<para lang=\"en\">\nEXTERNAL authentication mechanism in mail proxy\nwas accepted even if it was not enabled in the configuration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы ssl_verify_client модуля stream\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"ssl_verify_client\" directive of the stream module was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива ssl_verify_client модуля stream могла не работать.\n</para>\n<para lang=\"en\">\nthe \"ssl_verify_client\" directive of the stream module might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри исчерпании рабочим процессом свободных соединений\nkeepalive-соединения могли закрываться излишне агрессивно.<br/>\nСпасибо Joel Cunningham.\n</para>\n<para lang=\"en\">\nclosing keepalive connections due to no free worker connections\nmight be too aggressive.<br/>\nThanks to Joel Cunningham.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы sendfile на FreeBSD и macOS\nмог возвращаться некорректный ответ;\nошибка появилась в 1.7.8.\n</para>\n<para lang=\"en\">\nan incorrect response might be returned\nwhen using the \"sendfile\" directive on FreeBSD and macOS;\nthe bug had appeared in 1.7.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы aio_write\nответ мог сохраняться в кэш не полностью.\n</para>\n<para lang=\"en\">\na truncated response might be stored in cache\nwhen using the \"aio_write\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы aio_write\nмогла происходить утечка сокетов.\n</para>\n<para lang=\"en\">\na socket leak might occur\nwhen using the \"aio_write\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.8\" date=\"2016-12-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива absolute_redirect.\n</para>\n<para lang=\"en\">\nthe \"absolute_redirect\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр escape директивы log_format.\n</para>\n<para lang=\"en\">\nthe \"escape\" parameter of the \"log_format\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпроверка клиентских SSL-сертификатов в модуле stream.\n</para>\n<para lang=\"en\">\nclient SSL certificates verification in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_session_ticket_key поддерживает\nшифрование TLS session tickets с помощью AES256\nпри использовании с 80-байтными ключами.\n</para>\n<para lang=\"en\">\nthe \"ssl_session_ticket_key\" directive supports\nAES256 encryption of TLS session tickets\nwhen used with 80-byte keys.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка vim-commentary в скриптах для vim.<br/>\nСпасибо Armin Grodon.\n</para>\n<para lang=\"en\">\nvim-commentary support in vim scripts.<br/>\nThanks to Armin Grodon.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрекурсия при получении значений переменных не ограничивалась.\n</para>\n<para lang=\"en\">\nrecursion when evaluating variables was not limited.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_stream_ssl_preread_module.\n</para>\n<para lang=\"en\">\nin the ngx_stream_ssl_preread_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли сервер, описанный в блоке upstream в модуле stream,\nбыл признан неработающим, то после истечения fail_timeout он\nпризнавался работающим только после завершения тестового соединения;\nтеперь достаточно, чтобы соединение было успешно установлено.\n</para>\n<para lang=\"en\">\nif a server in an upstream in the stream module failed,\nit was considered alive only when a test connection sent\nto it after fail_timeout was closed;\nnow a successfully established connection is enough.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не собирался с 64-битным Visual Studio.\n</para>\n<para lang=\"en\">\nnginx/Windows could not be built with 64-bit Visual Studio.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не собирался с OpenSSL 1.1.0.\n</para>\n<para lang=\"en\">\nnginx/Windows could not be built with OpenSSL 1.1.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.7\" date=\"2016-12-13\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпеременная $ssl_client_verify теперь\nв случае ошибки проверки клиентского сертификата\nсодержит строку с описанием ошибки,\nнапример, \"FAILED:certificate has expired\".\n</para>\n<para lang=\"en\">\nnow in case of a client certificate verification error\nthe $ssl_client_verify variable contains a string with the failure reason,\nfor example, \"FAILED:certificate has expired\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $ssl_ciphers, $ssl_curves,\n$ssl_client_v_start, $ssl_client_v_end и $ssl_client_v_remain.\n</para>\n<para lang=\"en\">\nthe $ssl_ciphers, $ssl_curves,\n$ssl_client_v_start, $ssl_client_v_end, and $ssl_client_v_remain variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр volatile директивы map.\n</para>\n<para lang=\"en\">\nthe \"volatile\" parameter of the \"map\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри сборке динамических модулей\nне учитывались заданные для модуля зависимости.\n</para>\n<para lang=\"en\">\ndependencies specified for a module\nwere ignored while building dynamic modules.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 и директив limit_req или auth_request\nтело запроса могло быть повреждено;\nошибка появилась в 1.11.0.\n</para>\n<para lang=\"en\">\nwhen using HTTP/2 and the \"limit_req\" or \"auth_request\" directives\nclient request body might be corrupted;\nthe bug had appeared in 1.11.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 в рабочем процессе мог произойти segmentation fault;\nошибка появилась в 1.11.3.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process when using HTTP/2;\nthe bug had appeared in 1.11.3.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.<br/>\nСпасибо Congcong Hu.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.<br/>\nThanks to Congcong Hu.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_perl_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_perl_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.6\" date=\"2016-11-15\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nформат переменных $ssl_client_s_dn и $ssl_client_i_dn\nизменён на соответствующий RFC 2253 (RFC 4514);\nзначения в старом формате доступны через переменные\n$ssl_client_s_dn_legacy и $ssl_client_i_dn_legacy.\n</para>\n<para lang=\"en\">\nformat of the $ssl_client_s_dn and $ssl_client_i_dn variables\nhas been changed to follow RFC 2253 (RFC 4514);\nvalues in the old format are available in\nthe $ssl_client_s_dn_legacy and $ssl_client_i_dn_legacy variables.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпри сохранении временных файлов в каталоге кэша\nони теперь располагаются не в отдельном подкаталоге для временных файлов,\nа в том же подкаталоге, что и соответствующие файлы в кэше.\n</para>\n<para lang=\"en\">\nwhen storing temporary files in a cache directory\nthey will be stored in the same subdirectories as corresponding cache files\ninstead of a separate subdirectory for temporary files.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка метода аутентификации EXTERNAL\nв почтовом прокси-сервере.<br/>\nСпасибо Robert Norris.\n</para>\n<para lang=\"en\">\nEXTERNAL authentication mechanism support\nin mail proxy.<br/>\nThanks to Robert Norris.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка WebP в модуле ngx_http_image_filter_module.\n</para>\n<para lang=\"en\">\nWebP support in the ngx_http_image_filter_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_method поддерживает переменные.<br/>\nСпасибо Дмитрию Лазуркину.\n</para>\n<para lang=\"en\">\nvariables support in the \"proxy_method\" directive.<br/>\nThanks to Dmitry Lazurkin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива http2_max_requests в модуле ngx_http_v2_module.\n</para>\n<para lang=\"en\">\nthe \"http2_max_requests\" directive in the ngx_http_v2_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_cache_max_range_offset, fastcgi_cache_max_range_offset,\nscgi_cache_max_range_offset и uwsgi_cache_max_range_offset.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_max_range_offset\", \"fastcgi_cache_max_range_offset\",\n\"scgi_cache_max_range_offset\", and \"uwsgi_cache_max_range_offset\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nплавное завершение старых рабочих процессов могло занимать бесконечное время\nпри использовании HTTP/2.\n</para>\n<para lang=\"en\">\ngraceful shutdown of old worker processes might require infinite time\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании WebSocket-соединений и включённом кэшировании\nв логах могли появляться сообщения \"ignore long locked inactive cache entry\".\n</para>\n<para lang=\"en\">\n\"ignore long locked inactive cache entry\" alerts might appear in logs\nwhen proxying WebSocket connections with caching enabled.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли во время SSL handshake с бэкендом происходил таймаут,\nnginx ничего не писал в лог\nи возвращал ответ с кодом 502 вместо 504.\n</para>\n<para lang=\"en\">\nnginx did not write anything to log\nand returned a response with code 502 instead of 504\nwhen a timeout occurred during an SSL handshake to a backend.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.5\" date=\"2016-10-11\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр configure --with-ipv6 упразднён,\nподдержка IPv6 теперь собирается автоматически.\n</para>\n<para lang=\"en\">\nthe --with-ipv6 configure option was removed,\nnow IPv6 support is configured automatically.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь, если в блоке upstream не оказалось доступных серверов,\nnginx не сбрасывает статистику ошибок всех серверов, как делал ранее,\nа ожидает истечения fail_timeout.\n</para>\n<para lang=\"en\">\nnow if there are no available servers in an upstream,\nnginx will not reset number of failures of all servers as it previously did,\nbut will wait for fail_timeout to expire.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_ssl_preread_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_ssl_preread_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server в блоке upstream поддерживает параметр max_conns.\n</para>\n<para lang=\"en\">\nthe \"server\" directive in the \"upstream\" context supports\nthe \"max_conns\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр configure --with-compat.\n</para>\n<para lang=\"en\">\nthe --with-compat configure option.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры manager_files, manager_threshold и manager_sleep\nдиректив proxy_cache_path, fastcgi_cache_path, scgi_cache_path и\nuwsgi_cache_path.\n</para>\n<para lang=\"en\">\n\"manager_files\", \"manager_threshold\", and \"manager_sleep\" parameters\nof the \"proxy_cache_path\", \"fastcgi_cache_path\", \"scgi_cache_path\", and\n\"uwsgi_cache_path\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри сборке perl-модуля не использовались флаги,\nзаданные с помощью параметра configure --with-ld-opt.\n</para>\n<para lang=\"en\">\nflags passed by the --with-ld-opt configure option\nwere not used while building perl module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве add_after_body при использовании совместно с директивой sub_filter.\n</para>\n<para lang=\"en\">\nin the \"add_after_body\" directive when used with the \"sub_filter\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв переменной $realip_remote_addr.\n</para>\n<para lang=\"en\">\nin the $realip_remote_addr variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы dav_access, proxy_store_access, fastcgi_store_access,\nscgi_store_access и uwsgi_store_access\nигнорировали права, заданные для пользователя.\n</para>\n<para lang=\"en\">\nthe \"dav_access\", \"proxy_store_access\", \"fastcgi_store_access\",\n\"scgi_store_access\", and \"uwsgi_store_access\" directives\nignored permissions specified for user.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nunix domain listen-сокеты могли не наследоваться\nпри обновлении исполняемого файла на Linux.\n</para>\n<para lang=\"en\">\nunix domain listen sockets might not be inherited\nduring binary upgrade on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx возвращал ошибку 400 на запросы\nс символом \"-\" в HTTP-методе.\n</para>\n<para lang=\"en\">\nnginx returned the 400 response on requests\nwith the \"-\" character in the HTTP method.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.4\" date=\"2016-09-13\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $upstream_bytes_received.\n</para>\n<para lang=\"en\">\nthe $upstream_bytes_received variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $bytes_received, $session_time, $protocol, $status,\n$upstream_addr, $upstream_bytes_sent, $upstream_bytes_received,\n$upstream_connect_time, $upstream_first_byte_time\nи $upstream_session_time в модуле stream.\n</para>\n<para lang=\"en\">\nthe $bytes_received, $session_time, $protocol, $status,\n$upstream_addr, $upstream_bytes_sent, $upstream_bytes_received,\n$upstream_connect_time, $upstream_first_byte_time,\nand $upstream_session_time variables in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_log_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_log_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр proxy_protocol в директиве listen,\nпеременные $proxy_protocol_addr и $proxy_protocol_port\nв модуле stream.\n</para>\n<para lang=\"en\">\nthe \"proxy_protocol\" parameter of the \"listen\" directive,\nthe $proxy_protocol_addr and $proxy_protocol_port variables\nin the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_realip_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_realip_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с модулем stream и модулем ngx_http_ssl_module,\nно без модуля ngx_stream_ssl_module;\nошибка появилась в 1.11.3.\n</para>\n<para lang=\"en\">\nnginx could not be built with the stream module and the ngx_http_ssl_module,\nbut without ngx_stream_ssl_module;\nthe bug had appeared in 1.11.3.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nопция сокета IP_BIND_ADDRESS_NO_PORT не использовалась;\nошибка появилась в 1.11.2.\n</para>\n<para lang=\"en\">\nthe IP_BIND_ADDRESS_NO_PORT socket option was not used;\nthe bug had appeared in 1.11.2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв параметре ranges директивы geo.\n</para>\n<para lang=\"en\">\nin the \"ranges\" parameter of the \"geo\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директив \"aio threads\" и sendfile\nмог возвращаться некорректный ответ; ошибка появилась в 1.9.13.\n</para>\n<para lang=\"en\">\nan incorrect response might be returned\nwhen using the \"aio threads\" and \"sendfile\" directives;\nthe bug had appeared in 1.9.13.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.3\" date=\"2016-07-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь accept_mutex по умолчанию выключен.\n</para>\n<para lang=\"en\">\nnow the \"accept_mutex\" directive is turned off by default.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx использует EPOLLEXCLUSIVE на Linux.\n</para>\n<para lang=\"en\">\nnow nginx uses EPOLLEXCLUSIVE on Linux.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_geo_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_geo_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_geoip_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_geoip_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_split_clients_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_split_clients_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_pass и proxy_ssl_name в модуле stream\nподдерживают переменные.\n</para>\n<para lang=\"en\">\nvariables support\nin the \"proxy_pass\" and \"proxy_ssl_name\" directives in the stream module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов при использовании HTTP/2.\n</para>\n<para lang=\"en\">\nsocket leak when using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв configure.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin configure tests.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.2\" date=\"2016-07-05\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx всегда использует внутренние реализации MD5 и SHA1;\nпараметры configure --with-md5 и --with-sha1 упразднены.\n</para>\n<para lang=\"en\">\nnow nginx always uses internal MD5 and SHA1 implementations;\nthe --with-md5 and --with-sha1 configure options were canceled.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка переменных в модуле stream.\n</para>\n<para lang=\"en\">\nvariables support in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_map_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_map_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_stream_return_module.\n</para>\n<para lang=\"en\">\nthe ngx_stream_return_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв директивах proxy_bind, fastcgi_bind, memcached_bind, scgi_bind и uwsgi_bind\nтеперь можно указывать порт.\n</para>\n<para lang=\"en\">\na port can be specified in the \"proxy_bind\", \"fastcgi_bind\",\n\"memcached_bind\", \"scgi_bind\", and \"uwsgi_bind\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx использует опцию сокета IP_BIND_ADDRESS_NO_PORT, если она доступна.\n</para>\n<para lang=\"en\">\nnow nginx uses the IP_BIND_ADDRESS_NO_PORT socket option when available.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 и директивы proxy_request_buffering\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using HTTP/2 and the \"proxy_request_buffering\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nк запросам, передаваемым на бэкенд,\nвсегда добавлялась строка заголовка \"Content-Length\",\nдаже если у запроса не было тела.\n</para>\n<para lang=\"en\">\nthe \"Content-Length\" request header line\nwas always added to requests passed to backends,\nincluding requests without body,\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nв логах могли появляться сообщения \"http request count is zero\".\n</para>\n<para lang=\"en\">\n\"http request count is zero\" alerts might appear in logs\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы sub_filter\nмогло буферизироваться больше данных, чем это необходимо;\nпроблема появилась в 1.9.4.\n</para>\n<para lang=\"en\">\nunnecessary buffering might occur\nwhen using the \"sub_filter\" directive;\nthe issue had appeared in 1.9.4.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.1\" date=\"2016-05-31\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри записи тела специально созданного запроса во временный файл\nв рабочем процессе мог происходить segmentation fault\n(CVE-2016-4450);\nошибка появилась в 1.3.9.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhile writing a specially crafted request body to a temporary file\n(CVE-2016-4450);\nthe bug had appeared in 1.3.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.11.0\" date=\"2016-05-24\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр transparent директив proxy_bind, fastcgi_bind,\nmemcached_bind, scgi_bind и uwsgi_bind.\n</para>\n<para lang=\"en\">\nthe \"transparent\" parameter of the \"proxy_bind\", \"fastcgi_bind\",\n\"memcached_bind\", \"scgi_bind\", and \"uwsgi_bind\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $request_id.\n</para>\n<para lang=\"en\">\nthe $request_id variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива map поддерживает комбинации нескольких переменных\nв качестве результирующих значений.\n</para>\n<para lang=\"en\">\nthe \"map\" directive supports combinations of multiple variables\nas resulting values.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при использовании метода epoll\nnginx проверяет, поддерживает ли ядро события EPOLLRDHUP,\nи соответственно оптимизирует обработку соединений.\n</para>\n<para lang=\"en\">\nnow nginx checks if EPOLLRDHUP events are supported by kernel,\nand optimizes connection handling accordingly\nif the \"epoll\" method is used.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssl_certificate и ssl_certificate_key\nтеперь можно указывать несколько раз\nдля загрузки сертификатов разных типов (например, RSA и ECDSA).\n</para>\n<para lang=\"en\">\nthe \"ssl_certificate\" and \"ssl_certificate_key\" directives\ncan be specified multiple times\nto load certificates of different types (for example, RSA and ECDSA).\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпри использовании OpenSSL 1.0.2 и новее\nс помощью директивы ssl_ecdh_curve теперь можно задать список кривых;\nпо умолчанию используется встроенный в OpenSSL список кривых.\n</para>\n<para lang=\"en\">\nthe \"ssl_ecdh_curve\" directive now allows specifying a list of curves\nwhen using OpenSSL 1.0.2 or newer;\nby default a list built into OpenSSL is used.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдля использования DHE-шифров теперь надо явно задавать файл параметров\nс помощью директивы ssl_dhparam.\n</para>\n<para lang=\"en\">\nto use DHE ciphers it is now required to specify parameters\nusing the \"ssl_dhparam\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $proxy_protocol_port.\n</para>\n<para lang=\"en\">\nthe $proxy_protocol_port variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $realip_remote_port в модуле ngx_http_realip_module.\n</para>\n<para lang=\"en\">\nthe $realip_remote_port variable in the ngx_http_realip_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_realip_module теперь позволяет устанавливать\nне только адрес, но и порт клиента.\n</para>\n<para lang=\"en\">\nthe ngx_http_realip_module is now able to set the client port\nin addition to the address.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпри попытке запросить виртуальный сервер,\nотличающийся от согласованного в процессе SSL handshake,\nтеперь возвращается ответ \"421 Misdirected Request\";\nэто улучшает совместимость с некоторыми HTTP/2-клиентами\nв случае использования клиентских сертификатов.\n</para>\n<para lang=\"en\">\nthe \"421 Misdirected Request\" response now used\nwhen rejecting requests to a virtual server\ndifferent from one negotiated during an SSL handshake;\nthis improves interoperability with some HTTP/2 clients\nwhen using client certificates.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nHTTP/2-клиенты теперь могут сразу присылать тело запроса;\nдиректива http2_body_preread_size позволяет указать размер буфера, который\nбудет использоваться до того, как nginx начнёт читать тело.\n</para>\n<para lang=\"en\">\nHTTP/2 clients can now start sending request body immediately;\nthe \"http2_body_preread_size\" directive controls size of the buffer used\nbefore nginx will start reading client request body.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы proxy_cache_bypass\nне обновлялись закэшированные ошибочные ответы.\n</para>\n<para lang=\"en\">\ncached error responses were not updated\nwhen using the \"proxy_cache_bypass\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.15\" date=\"2016-04-19\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HHVM в качестве FastCGI-сервера\nмогли возникать ошибки \"recv() failed\".\n</para>\n<para lang=\"en\">\n\"recv() failed\" errors might occur\nwhen using HHVM as a FastCGI server.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 и директив limit_req или auth_request\nпри чтении тела запроса мог произойти таймаут\nили ошибка \"client violated flow control\";\nошибка появилась в 1.9.14.\n</para>\n<para lang=\"en\">\nwhen using HTTP/2 and the \"limit_req\" or \"auth_request\" directives\na timeout or a \"client violated flow control\" error\nmight occur while reading client request body;\nthe bug had appeared in 1.9.14.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпри использовании HTTP/2 ответ мог не показываться некоторыми браузерами,\nесли тело запроса было прочитано не целиком;\nошибка появилась в 1.9.14.\n</para>\n<para lang=\"en\">\na response might not be shown by some browsers\nif HTTP/2 was used and client request body was not fully read;\nthe bug had appeared in 1.9.14.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы \"aio threads\" соединения могли зависать.<br/>\nСпасибо Mindaugas Rasiukevicius.\n</para>\n<para lang=\"en\">\nconnections might hang when using the \"aio threads\" directive.<br/>\nThanks to Mindaugas Rasiukevicius.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.14\" date=\"2016-04-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nсовместимость с OpenSSL 1.1.0.\n</para>\n<para lang=\"en\">\nOpenSSL 1.1.0 compatibility.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_request_buffering, fastcgi_request_buffering,\nscgi_request_buffering и uwsgi_request_buffering\nтеперь работают при использовании HTTP/2.\n</para>\n<para lang=\"en\">\nthe \"proxy_request_buffering\", \"fastcgi_request_buffering\",\n\"scgi_request_buffering\", and \"uwsgi_request_buffering\" directives\nnow work with HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nв логах могли появляться сообщения \"zero size buf in output\".\n</para>\n<para lang=\"en\">\n\"zero size buf in output\" alerts might appear in logs\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nдиректива client_max_body_size могла работать неверно.\n</para>\n<para lang=\"en\">\nthe \"client_max_body_size\" directive might work incorrectly\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнезначительных ошибок логгирования.\n</para>\n<para lang=\"en\">\nof minor bugs in logging.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.13\" date=\"2016-03-29\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nнеидемпотентные запросы (POST, LOCK, PATCH)\nтеперь по умолчанию не передаются на другой сервер,\nесли запрос уже был отправлен на бэкенд;\nпараметр non_idempotent директивы proxy_next_upstream\nявно разрешает повторять такие запросы.\n</para>\n<para lang=\"en\">\nnon-idempotent requests (POST, LOCK, PATCH)\nare no longer passed to the next server by default\nif a request has been sent to a backend;\nthe \"non_idempotent\" parameter of the \"proxy_next_upstream\" directive\nexplicitly allows retrying such requests.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module теперь можно собрать динамически.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module can be built dynamically.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка UDP в модуле stream.\n</para>\n<para lang=\"en\">\nUDP support in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива aio_write.\n</para>\n<para lang=\"en\">\nthe \"aio_write\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь cache manager следит за количеством элементов в кэше\nи старается не допускать переполнений зоны разделяемой памяти.\n</para>\n<para lang=\"en\">\nnow cache manager monitors number of elements in caches\nand tries to avoid cache keys zone overflows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директив sendfile и aio с подзапросами\nв логах могли появляться сообщения \"task already active\" и \"second aio post\".\n</para>\n<para lang=\"en\">\n\"task already active\" and \"second aio post\" alerts might appear in logs\nwhen using the \"sendfile\" and \"aio\" directives with subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании кэширования\nв логах могли появляться сообщения \"zero size buf in output\",\nесли клиент закрывал соединение преждевременно.\n</para>\n<para lang=\"en\">\n\"zero size buf in output\" alerts might appear in logs\nif caching was used\nand a client closed a connection prematurely.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании кэширования\nсоединения с клиентами могли закрываться без необходимости.<br/>\nСпасибо Justin Li.\n</para>\n<para lang=\"en\">\nconnections with clients might be closed needlessly\nif caching was used.<br/>\nThanks to Justin Li.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог нагружать процессор\nпри использовании директивы sendfile на Linux и Solaris,\nесли отправляемый файл был изменён в процессе отправки.\n</para>\n<para lang=\"en\">\nnginx might hog CPU\nif the \"sendfile\" directive was used on Linux or Solaris\nand a file being sent was changed during sending.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директив sendfile и \"aio threads\"\nсоединения могли зависать.\n</para>\n<para lang=\"en\">\nconnections might hang\nwhen using the \"sendfile\" and \"aio threads\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директивах proxy_pass, fastcgi_pass, scgi_pass и uwsgi_pass\nпри использовании переменных.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the \"proxy_pass\", \"fastcgi_pass\", \"scgi_pass\", and \"uwsgi_pass\" directives\nwhen using variables.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_sub_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_sub_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в закэшированном соединении к бэкенду происходила ошибка,\nзапрос передавался на другой сервер\nбез учёта директивы proxy_next_upstream.\n</para>\n<para lang=\"en\">\nif an error occurred in a cached backend connection,\nthe request was passed to the next server\nregardless of the proxy_next_upstream directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"CreateFile() failed\" при создании временных файлов на Windows.\n</para>\n<para lang=\"en\">\n\"CreateFile() failed\" errors when creating temporary files on Windows.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.12\" date=\"2016-02-24\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nкодирование Хаффмана заголовков ответов в HTTP/2.<br/>\nСпасибо Владу Краснову.\n</para>\n<para lang=\"en\">\nHuffman encoding of response headers in HTTP/2.<br/>\nThanks to Vlad Krasnov.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_cpu_affinity теперь поддерживает более 64 процессоров.\n</para>\n<para lang=\"en\">\nthe \"worker_cpu_affinity\" directive now supports more than 64 CPUs.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость со сторонними модулями на C++;\nошибка появилась в 1.9.11.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\ncompatibility with 3rd party C++ modules;\nthe bug had appeared in 1.9.11.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался статически с OpenSSL на Linux;\nошибка появилась в 1.9.11.\n</para>\n<para lang=\"en\">\nnginx could not be built statically with OpenSSL on Linux;\nthe bug had appeared in 1.9.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"add_header ... always\" с пустым значением\nне удаляла из заголовков ошибочных ответов\nстроки Last-Modified и ETag.\n</para>\n<para lang=\"en\">\nthe \"add_header ... always\" directive with an empty value\ndid not delete \"Last-Modified\" and \"ETag\" header lines\nfrom error responses.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпри использовании OpenSSL 1.0.2f в логах могли появляться\nсообщения \"called a function you should not call\" и\n\"shutdown while in init\".\n</para>\n<para lang=\"en\">\n\"called a function you should not call\"\nand \"shutdown while in init\" messages might appear in logs\nwhen using OpenSSL 1.0.2f.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибочные заголовки могли логгироваться некорректно.\n</para>\n<para lang=\"en\">\ninvalid headers might be logged incorrectly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов при использовании HTTP/2.\n</para>\n<para lang=\"en\">\nsocket leak when using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_v2_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_v2_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.11\" date=\"2016-02-09\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь resolver поддерживает TCP.\n</para>\n<para lang=\"en\">\nTCP support in resolver.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдинамические модули.\n</para>\n<para lang=\"en\">\ndynamic modules.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nпеременная $request_length не учитывала размер заголовков запроса.\n</para>\n<para lang=\"en\">\nthe $request_length variable did not include size of request headers\nwhen using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_v2_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_v2_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.10\" date=\"2016-01-26\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании директивы resolver\nво время обработки ответов DNS-сервера\nмогло происходить разыменование некорректного адреса,\nчто позволяло атакующему,\nимеющему возможность подделывать UDP-пакеты от DNS-сервера,\nвызвать segmentation fault в рабочем процессе (CVE-2016-0742).\n</para>\n<para lang=\"en\">\ninvalid pointer dereference might occur\nduring DNS server response processing\nif the \"resolver\" directive was used,\nallowing an attacker who is able to forge UDP packets from the DNS server\nto cause segmentation fault in a worker process (CVE-2016-0742).\n</para>\n</change>\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании директивы resolver\nво время обработки CNAME-записей\nмогло произойти обращение к ранее освобождённой памяти,\nчто позволяло атакующему,\nимеющему возможность инициировать преобразование произвольных имён в адреса,\nвызвать segmentation fault в рабочем процессе,\nа также потенциально могло иметь другие последствия (CVE-2016-0746).\n</para>\n<para lang=\"en\">\nuse-after-free condition might occur\nduring CNAME response processing\nif the \"resolver\" directive was used,\nallowing an attacker who is able to trigger name resolution\nto cause segmentation fault in a worker process,\nor might have potential other impact (CVE-2016-0746).\n</para>\n</change>\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании директивы resolver\nво время обработки CNAME-записей\nне во всех случаях проверялось ограничение\nна максимальное количество записей в цепочке,\nчто позволяло атакующему,\nимеющему возможность инициировать преобразование произвольных имён в адреса,\nвызвать чрезмерное потребление ресурсов рабочими процессами (CVE-2016-0747).\n</para>\n<para lang=\"en\">\nCNAME resolution was insufficiently limited\nif the \"resolver\" directive was used,\nallowing an attacker who is able to trigger arbitrary name resolution\nto cause excessive resource consumption in worker processes (CVE-2016-0747).\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр auto директивы worker_cpu_affinity.\n</para>\n<para lang=\"en\">\nthe \"auto\" parameter of the \"worker_cpu_affinity\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр proxy_protocol директивы listen не работал\nс IPv6 listen-сокетами.\n</para>\n<para lang=\"en\">\nthe \"proxy_protocol\" parameter of the \"listen\" directive did not work\nwith IPv6 listen sockets.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы keepalive\nсоединения к бэкендам могли кэшироваться некорректно.\n</para>\n<para lang=\"en\">\nconnections to upstream servers might be cached incorrectly\nwhen using the \"keepalive\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле перенаправления запроса с помощью X-Accel-Redirect\nпри проксировании использовался HTTP-метод оригинального запроса.\n</para>\n<para lang=\"en\">\nproxying used the HTTP method of the original request\nafter an \"X-Accel-Redirect\" redirection.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.9\" date=\"2015-12-09\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроксирование в unix domain сокеты не работало при использовании переменных;\nошибка появилась в 1.9.8.\n</para>\n<para lang=\"en\">\nproxying to unix domain sockets did not work when using variables;\nthe bug had appeared in 1.9.8.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.8\" date=\"2015-12-08\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка pwritev().\n</para>\n<para lang=\"en\">\npwritev() support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива include в блоке upstream.\n</para>\n<para lang=\"en\">\nthe \"include\" directive inside the \"upstream\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_slice_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_slice_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании LibreSSL\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 1.9.6.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using LibreSSL;\nthe bug had appeared in 1.9.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог не собираться на OS X.\n</para>\n<para lang=\"en\">\nnginx could not be built on OS X in some cases.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.7\" date=\"2015-11-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр nohostname логгирования в syslog.\n</para>\n<para lang=\"en\">\nthe \"nohostname\" parameter of logging to syslog.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_cache_convert_head.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_convert_head\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $realip_remote_addr в модуле ngx_http_realip_module.\n</para>\n<para lang=\"en\">\nthe $realip_remote_addr variable in the ngx_http_realip_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива expires могла не срабатывать при использовании переменных.\n</para>\n<para lang=\"en\">\nthe \"expires\" directive might not work when using variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 1.9.6.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using HTTP/2;\nthe bug had appeared in 1.9.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx был собран с модулем ngx_http_v2_module,\nпротокол HTTP/2 мог быть использован клиентом,\nдаже если не был указан параметр http2 директивы listen.\n</para>\n<para lang=\"en\">\nif nginx was built with the ngx_http_v2_module\nit was possible to use the HTTP/2 protocol\neven if the \"http2\" parameter of the \"listen\" directive was not specified.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_v2_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_v2_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.6\" date=\"2015-10-27\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2\nв рабочем процессе мог произойти segmentation fault.<br/>\nСпасибо Piotr Sikora и Denis Andzakovic.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using HTTP/2.<br/>\nThanks to Piotr Sikora and Denis Andzakovic.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP/2 переменная $server_protocol была пустой.\n</para>\n<para lang=\"en\">\nthe $server_protocol variable was empty when using HTTP/2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL-соединения к бэкендам в модуле stream\nмогли неожиданно завершаться по таймауту.\n</para>\n<para lang=\"en\">\nbackend SSL connections in the stream module\nmight be timed out unexpectedly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании различных настроек ssl_session_cache\nв разных виртуальных серверах\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif different ssl_session_cache settings were used\nin different virtual servers.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не собирался с MinGW gcc;\nошибка появилась в 1.9.4.<br/>\nСпасибо Kouhei Sutou.\n</para>\n<para lang=\"en\">\nnginx/Windows could not be built with MinGW gcc;\nthe bug had appeared in 1.9.4.<br/>\nThanks to Kouhei Sutou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы timer_resolution на Windows время не обновлялось.\n</para>\n<para lang=\"en\">\ntime was not updated when the timer_resolution directive was used on Windows.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nНезначительные исправления и улучшения.<br/>\nСпасибо Markus Linnala, Kurtis Nusbaum и Piotr Sikora.\n</para>\n<para lang=\"en\">\nMiscellaneous minor fixes and improvements.<br/>\nThanks to Markus Linnala, Kurtis Nusbaum and Piotr Sikora.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.5\" date=\"2015-09-22\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_v2_module (заменяет модуль ngx_http_spdy_module).<br/>\nСпасибо Dropbox и Automattic за спонсирование разработки.\n</para>\n<para lang=\"en\">\nthe ngx_http_v2_module (replaces ngx_http_spdy_module).<br/>\nThanks to Dropbox and Automattic for sponsoring this work.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию директива output_buffers использует два буфера.\n</para>\n<para lang=\"en\">\nnow the \"output_buffers\" directive uses two buffers by default.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx ограничивает максимальную вложенность подзапросов,\nа не количество одновременных подзапросов.\n</para>\n<para lang=\"en\">\nnow nginx limits subrequests recursion,\nnot simultaneous subrequests.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при возврате ответов из кэша nginx проверяет ключ полностью.<br/>\nСпасибо Геннадию Махомеду и Сергею Брестеру.\n</para>\n<para lang=\"en\">\nnow nginx checks the whole cache key when returning a response from cache.<br/>\nThanks to Gena Makhomed and Sergey Brester.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании кэша\nв логах могли появляться сообщения \"header already sent\";\nошибка появилась в 1.7.5.\n</para>\n<para lang=\"en\">\n\"header already sent\" alerts might appear in logs\nwhen using cache;\nthe bug had appeared in 1.7.5.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании CephFS и директивы timer_resolution на Linux\nв логах могли появляться сообщения\n\"writev() failed (4: Interrupted system call)\".\n</para>\n<para lang=\"en\">\n\"writev() failed (4: Interrupted system call)\"\nerrors might appear in logs\nwhen using CephFS and the \"timer_resolution\" directive on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок конфигурации.<br/>\nСпасибо Markus Linnala.\n</para>\n<para lang=\"en\">\nin invalid configurations handling.<br/>\nThanks to Markus Linnala.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы sub_filter на уровне http\nв рабочем процессе происходил segmentation fault;\nошибка появилась в 1.9.4.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in a worker process\nif the \"sub_filter\" directive was used at http level;\nthe bug had appeared in 1.9.4.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.4\" date=\"2015-08-18\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы proxy_downstream_buffer и proxy_upstream_buffer в модуле stream\nзаменены директивой proxy_buffer_size.\n</para>\n<para lang=\"en\">\nthe \"proxy_downstream_buffer\" and \"proxy_upstream_buffer\" directives\nof the stream module are replaced with the \"proxy_buffer_size\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива tcp_nodelay в модуле stream.\n</para>\n<para lang=\"en\">\nthe \"tcp_nodelay\" directive in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь можно указать несколько директив sub_filter одновременно.\n</para>\n<para lang=\"en\">\nmultiple \"sub_filter\" directives can be used simultaneously.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива sub_filter поддерживает переменные в строке поиска.\n</para>\n<para lang=\"en\">\nvariables support in the search string of the \"sub_filter\" directive.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nтестирование конфигурации могло не работать под Linux OpenVZ.<br/>\nСпасибо Геннадию Махомеду.\n</para>\n<para lang=\"en\">\nconfiguration testing might fail under Linux OpenVZ.<br/>\nThanks to Gena Makhomed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле переконфигурации старые рабочие процессы могли сильно нагружать процессор\nпри больших значениях worker_connections.\n</para>\n<para lang=\"en\">\nold worker processes might hog CPU after reconfiguration\nwith a large number of worker_connections.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри совместном использовании директив try_files и alias\nвнутри location'а, заданного регулярным выражением,\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 1.7.1.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"try_files\" and \"alias\" directives were used\ninside a location given by a regular expression;\nthe bug had appeared in 1.7.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива try_files внутри вложенного location'а, заданного регулярным\nвыражением, работала неправильно, если во внешнем location'е использовалась\nдиректива alias.\n</para>\n<para lang=\"en\">\nthe \"try_files\" directive inside a nested location\ngiven by a regular expression worked incorrectly\nif the \"alias\" directive was used in the outer location.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок при построении хэш-таблиц.\n</para>\n<para lang=\"en\">\nin hash table initialization error handling.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с Visual Studio 2015.\n</para>\n<para lang=\"en\">\nnginx could not be built with Visual Studio 2015.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.3\" date=\"2015-07-14\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдублирующиеся блоки http, mail и stream теперь запрещены.\n</para>\n<para lang=\"en\">\nduplicate \"http\", \"mail\", and \"stream\" blocks are now disallowed.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nограничение количества соединений в модуле stream.\n</para>\n<para lang=\"en\">\nconnection limiting in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nограничение скорости в модуле stream.\n</para>\n<para lang=\"en\">\ndata rate limiting in the stream module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива zone в блоке upstream не работала на Windows.\n</para>\n<para lang=\"en\">\nthe \"zone\" directive inside the \"upstream\" block did not work on Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с LibreSSL в модуле stream.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\ncompatibility with LibreSSL in the stream module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв параметре --builddir в configure.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the \"--builddir\" configure parameter.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива ssl_stapling_file не работала;\nошибка появилась в 1.9.2.<br/>\nСпасибо Faidon Liambotis и Brandon Black.\n</para>\n<para lang=\"en\">\nthe \"ssl_stapling_file\" directive did not work;\nthe bug had appeared in 1.9.2.<br/>\nThanks to Faidon Liambotis and Brandon Black.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы ssl_stapling\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 1.9.2.<br/>\nСпасибо Matthew Baldwin.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"ssl_stapling\" directive was used;\nthe bug had appeared in 1.9.2.<br/>\nThanks to Matthew Baldwin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.2\" date=\"2015-06-16\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр backlog директивы listen\nв почтовом прокси-сервере и модуле stream.\n</para>\n<para lang=\"en\">\nthe \"backlog\" parameter of the \"listen\" directives\nof the mail proxy and stream modules.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы allow и deny в модуле stream.\n</para>\n<para lang=\"en\">\nthe \"allow\" and \"deny\" directives in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_bind в модуле stream.\n</para>\n<para lang=\"en\">\nthe \"proxy_bind\" directive in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_protocol в модуле stream.\n</para>\n<para lang=\"en\">\nthe \"proxy_protocol\" directive in the stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -T.\n</para>\n<para lang=\"en\">\nthe -T switch.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр REQUEST_SCHEME добавлен в стандартные конфигурационные файлы\nfastcgi.conf, fastcgi_params, scgi_params и uwsgi_params.\n</para>\n<para lang=\"en\">\nthe REQUEST_SCHEME parameter added to the fastcgi.conf, fastcgi_params,\nscgi_params, and uwsgi_params standard configuration files.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр reuseport директивы listen в модуле stream\nне работал.\n</para>\n<para lang=\"en\">\nthe \"reuseport\" parameter of the \"listen\" directive of the stream module\ndid not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nOCSP stapling в некоторых случаях мог вернуть устаревший OCSP-ответ.\n</para>\n<para lang=\"en\">\nOCSP stapling might return an expired OCSP response in some cases.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.1\" date=\"2015-05-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь протокол SSLv3 по умолчанию запрещён.\n</para>\n<para lang=\"en\">\nnow SSLv3 protocol is disabled by default.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nнекоторые давно устаревшие директивы больше не поддерживаются.\n</para>\n<para lang=\"en\">\nsome long deprecated directives are not supported anymore.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр reuseport директивы listen.<br/>\nСпасибо Yingqi Lu из Intel и Sepherosa Ziehau.\n</para>\n<para lang=\"en\">\nthe \"reuseport\" parameter of the \"listen\" directive.<br/>\nThanks to Yingqi Lu at Intel and Sepherosa Ziehau.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $upstream_connect_time.\n</para>\n<para lang=\"en\">\nthe $upstream_connect_time variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве hash на big-endian платформах.\n</para>\n<para lang=\"en\">\nin the \"hash\" directive on big-endian platforms.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог не запускаться на некоторых старых версиях Linux;\nошибка появилась в 1.7.11.\n</para>\n<para lang=\"en\">\nnginx might fail to start on some old Linux variants;\nthe bug had appeared in 1.7.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв парсинге IP-адресов.<br/>\nСпасибо Сергею Половко.\n</para>\n<para lang=\"en\">\nin IP address parsing.<br/>\nThanks to Sergey Polovko.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.9.0\" date=\"2015-04-28\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nустаревшие методы обработки соединений aio и rtsig больше не поддерживаются.\n</para>\n<para lang=\"en\">\nobsolete aio and rtsig event methods have been removed.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива zone в блоке upstream.\n</para>\n<para lang=\"en\">\nthe \"zone\" directive inside the \"upstream\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль stream.\n</para>\n<para lang=\"en\">\nthe stream module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка byte ranges для ответов модуля ngx_http_memcached_module.<br/>\nСпасибо Martin Mlynář.\n</para>\n<para lang=\"en\">\nbyte ranges support in the ngx_http_memcached_module.<br/>\nThanks to Martin Mlynář.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nразделяемую память теперь можно использовать на версиях Windows\nс рандомизацией адресного пространства.<br/>\nСпасибо Сергею Брестеру.\n</para>\n<para lang=\"en\">\nshared memory can now be used on Windows versions\nwith address space layout randomization.<br/>\nThanks to Sergey Brester.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу error_log теперь можно использовать\nна уровнях mail и server в почтовом прокси-сервере.\n</para>\n<para lang=\"en\">\nthe \"error_log\" directive can now be used\non mail and server levels in mail proxy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр proxy_protocol директивы listen не работал,\nесли не был указан в первой директиве listen для данного listen-сокета.\n</para>\n<para lang=\"en\">\nthe \"proxy_protocol\" parameter of the \"listen\" directive did not work\nif not specified in the first \"listen\" directive for a listen socket.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.12\" date=\"2015-04-07\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива tcp_nodelay работает для SSL-соединений с бэкендами.\n</para>\n<para lang=\"en\">\nnow the \"tcp_nodelay\" directive works with backend SSL connections.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь потоки могут использоваться для чтения заголовков файлов в кэше.\n</para>\n<para lang=\"en\">\nnow thread pools can be used to read cache file headers.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_request_buffering.\n</para>\n<para lang=\"en\">\nin the \"proxy_request_buffering\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании потоков на Linux\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen using thread pools on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок при использовании директивы ssl_stapling.<br/>\nСпасибо Filipe da Silva.\n</para>\n<para lang=\"en\">\nin error handling when using the \"ssl_stapling\" directive.<br/>\nThanks to Filipe da Silva.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.11\" date=\"2015-03-24\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр sendfile директивы aio более не нужен;\nтеперь nginx автоматически использует AIO для подгрузки данных для sendfile,\nесли одновременно используются директивы aio и sendfile.\n</para>\n<para lang=\"en\">\nthe \"sendfile\" parameter of the \"aio\" directive is deprecated;\nnow nginx automatically uses AIO to pre-load data for sendfile\nif both \"aio\" and \"sendfile\" directives are used.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nэкспериментальная поддержка потоков.\n</para>\n<para lang=\"en\">\nexperimental thread pools support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_request_buffering, fastcgi_request_buffering,\nscgi_request_buffering и uwsgi_request_buffering.\n</para>\n<para lang=\"en\">\nthe \"proxy_request_buffering\", \"fastcgi_request_buffering\",\n\"scgi_request_buffering\", and \"uwsgi_request_buffering\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nэкспериментальное API для обработки тела запроса.\n</para>\n<para lang=\"en\">\nrequest body filters experimental API.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпроверка клиентских SSL-сертификатов в почтовом прокси-сервере.<br/>\nСпасибо Sven Peter, Franck Levionnois и Filipe Da Silva.\n</para>\n<para lang=\"en\">\nclient SSL certificates support in mail proxy.<br/>\nThanks to Sven Peter, Franck Levionnois, and Filipe Da Silva.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nуменьшение времени запуска\nпри использовании директивы \"hash ... consistent\" в блоке upstream.<br/>\nСпасибо Wai Keen Woon.\n</para>\n<para lang=\"en\">\nstartup speedup\nwhen using the \"hash ... consistent\" directive in the upstream block.<br/>\nThanks to Wai Keen Woon.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nотладочное логгирование в кольцевой буфер в памяти.\n</para>\n<para lang=\"en\">\ndebug logging into a cyclic memory buffer.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке хэш-таблиц.<br/>\nСпасибо Chris West.\n</para>\n<para lang=\"en\">\nin hash table handling.<br/>\nThanks to Chris West.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_cache_revalidate.\n</para>\n<para lang=\"en\">\nin the \"proxy_cache_revalidate\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL-соединения могли зависать, если использовался отложенный accept\nили параметр proxy_protocol директивы listen.<br/>\nСпасибо James Hamlin.\n</para>\n<para lang=\"en\">\nSSL connections might hang if deferred accept\nor the \"proxy_protocol\" parameter of the \"listen\" directive were used.<br/>\nThanks to James Hamlin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $upstream_response_time могла содержать неверное значение\nпри использовании директивы image_filter.\n</para>\n<para lang=\"en\">\nthe $upstream_response_time variable might contain a wrong value\nif the \"image_filter\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке целочисленных переполнений.<br/>\nСпасибо Régis Leroy.\n</para>\n<para lang=\"en\">\nin integer overflow handling.<br/>\nThanks to Régis Leroy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании LibreSSL было невозможно включить поддержку SSLv3.\n</para>\n<para lang=\"en\">\nit was not possible to enable SSLv3 with LibreSSL.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании LibreSSL в логах появлялись сообщения\n\"ignoring stale global SSL error ... called a function you should not call\".\n</para>\n<para lang=\"en\">\nthe \"ignoring stale global SSL error ... called a function you should not call\"\nalerts appeared in logs when using LibreSSL.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсертификаты, указанные в директивах ssl_client_certificate и\nssl_trusted_certificate, использовались\nдля автоматического построения цепочек сертификатов.\n</para>\n<para lang=\"en\">\ncertificates specified by the \"ssl_client_certificate\" and\n\"ssl_trusted_certificate\" directives were inadvertently used\nto automatically construct certificate chains.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.10\" date=\"2015-02-10\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр use_temp_path директив proxy_cache_path, fastcgi_cache_path,\nscgi_cache_path и uwsgi_cache_path.\n</para>\n<para lang=\"en\">\nthe \"use_temp_path\" parameter of the \"proxy_cache_path\", \"fastcgi_cache_path\",\n\"scgi_cache_path\", and \"uwsgi_cache_path\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $upstream_header_time.\n</para>\n<para lang=\"en\">\nthe $upstream_header_time variable.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nтеперь при переполнении диска nginx пытается писать error_log'и только\nраз в секунду.\n</para>\n<para lang=\"en\">\nnow on disk overflow nginx tries to write error logs once a second only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива try_files при тестировании каталогов\nне игнорировала обычные файлы.<br/>\nСпасибо Damien Tournoud.\n</para>\n<para lang=\"en\">\nthe \"try_files\" directive did not ignore normal files\nwhile testing directories.<br/>\nThanks to Damien Tournoud.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы sendfile на OS X\nвозникали ошибки \"sendfile() failed\";\nошибка появилась в nginx 1.7.8.\n</para>\n<para lang=\"en\">\nalerts \"sendfile() failed\"\nif the \"sendfile\" directive was used on OS X;\nthe bug had appeared in 1.7.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв лог могли писаться сообщения \"sem_post() failed\".\n</para>\n<para lang=\"en\">\nalerts \"sem_post() failed\" might appear in logs.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с musl libc.<br/>\nСпасибо James Taylor.\n</para>\n<para lang=\"en\">\nnginx could not be built with musl libc.<br/>\nThanks to James Taylor.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Tru64 UNIX.<br/>\nСпасибо Goetz T. Fischer.\n</para>\n<para lang=\"en\">\nnginx could not be built on Tru64 UNIX.<br/>\nThanks to Goetz T. Fischer.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.9\" date=\"2014-12-23\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_cache, fastcgi_cache, scgi_cache и uwsgi_cache\nподдерживают переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"proxy_cache\", \"fastcgi_cache\", \"scgi_cache\",\nand \"uwsgi_cache\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива expires поддерживает переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"expires\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nвозможность загрузки секретных ключей с аппаратных устройств\nс помощью OpenSSL engines.<br/>\nСпасибо Дмитрию Пичулину.\n</para>\n<para lang=\"en\">\nloading of secret keys from hardware tokens\nwith OpenSSL engines.<br/>\nThanks to Dmitrii Pichulin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива autoindex_format.\n</para>\n<para lang=\"en\">\nthe \"autoindex_format\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nревалидация элементов кэша теперь используется только для ответов\nс кодами 200 и 206.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\ncache revalidation is now only used for responses\nwith 200 and 206 status codes.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстрока \"TE\" заголовка запроса клиента передавалась на бэкенд при проксировании.\n</para>\n<para lang=\"en\">\nthe \"TE\" client request header line was passed to backends while proxying.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_pass, fastcgi_pass, scgi_pass и uwsgi_pass\nмогли неправильно работать внутри блоков if и limit_except.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\", \"fastcgi_pass\", \"scgi_pass\", and \"uwsgi_pass\" directives\nmight not work correctly inside the \"if\" and \"limit_except\" blocks.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_store с параметром \"on\" игнорировалась,\nесли на предыдущем уровне использовалась директива proxy_store\nс явно заданным путём к файлам.\n</para>\n<para lang=\"en\">\nthe \"proxy_store\" directive with the \"on\" parameter was ignored\nif the \"proxy_store\" directive with an explicitly specified file path\nwas used on a previous level.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с BoringSSL.<br/>\nСпасибо Lukas Tribus.\n</para>\n<para lang=\"en\">\nnginx could not be built with BoringSSL.<br/>\nThanks to Lukas Tribus.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.8\" date=\"2014-12-02\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь строки \"If-Modified-Since\", \"If-Range\" и им подобные\nв заголовке запроса клиента передаются бэкенду при включённом кэшировании,\nесли nginx заранее знает, что не будет кэшировать ответ\n(например, при использовании proxy_cache_min_uses).\n</para>\n<para lang=\"en\">\nnow the \"If-Modified-Since\", \"If-Range\", etc.\nclient request header lines are passed to a backend while caching\nif nginx knows in advance that the response will not be cached\n(e.g., when using proxy_cache_min_uses).\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь после истечения proxy_cache_lock_timeout\nnginx отправляет запрос на бэкенд без кэширования;\nновые директивы proxy_cache_lock_age, fastcgi_cache_lock_age,\nscgi_cache_lock_age и uwsgi_cache_lock_age позволяют указать,\nчерез какое время блокировка будет принудительно снята\nи будет сделана ещё одна попытка закэшировать ответ.\n</para>\n<para lang=\"en\">\nnow after proxy_cache_lock_timeout\nnginx sends a request to a backend with caching disabled;\nthe new directives \"proxy_cache_lock_age\", \"fastcgi_cache_lock_age\",\n\"scgi_cache_lock_age\", and \"uwsgi_cache_lock_age\" specify a time\nafter which the lock will be released\nand another attempt to cache a response will be made.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива log_format теперь может использоваться только на уровне http.\n</para>\n<para lang=\"en\">\nthe \"log_format\" directive can now be used only at http level.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_ssl_certificate, proxy_ssl_certificate_key,\nproxy_ssl_password_file, uwsgi_ssl_certificate,\nuwsgi_ssl_certificate_key и uwsgi_ssl_password_file.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe \"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\" directives.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь с помощью X-Accel-Redirect\nможно перейти в именованный location.<br/>\nСпасибо Toshikuni Fukaya.\n</para>\n<para lang=\"en\">\nit is now possible to switch to a named location\nusing \"X-Accel-Redirect\".<br/>\nThanks to Toshikuni Fukaya.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива tcp_nodelay работает для SPDY-соединений.\n</para>\n<para lang=\"en\">\nnow the \"tcp_nodelay\" directive works with SPDY connections.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновые директивы в скриптах подсветки синтаксиса для vim.<br/>\nСпасибо Peter Wu.\n</para>\n<para lang=\"en\">\nnew directives in vim syntax highliting scripts.<br/>\nThanks to Peter Wu.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx игнорировал значение \"s-maxage\"\nв строке \"Cache-Control\" в заголовке ответа бэкенда.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nnginx ignored the \"s-maxage\" value\nin the \"Cache-Control\" backend response header line.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве ssl_password_file\nпри использовании OpenSSL 0.9.8zc, 1.0.0o, 1.0.1j.\n</para>\n<para lang=\"en\">\nin the \"ssl_password_file\" directive\nwhen using OpenSSL 0.9.8zc, 1.0.0o, 1.0.1j.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы post_action\nв лог писались сообщения \"header already sent\";\nошибка появилась в nginx 1.5.4.\n</para>\n<para lang=\"en\">\nalerts \"header already sent\" appeared in logs\nif the \"post_action\" directive was used;\nthe bug had appeared in 1.5.4.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы \"postpone_output 0\" с SSI-подзапросами\nв лог могли писаться сообщения \"the http output chain is empty\".\n</para>\n<para lang=\"en\">\nalerts \"the http output chain is empty\" might appear in logs\nif the \"postpone_output 0\" directive was used with SSI includes.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_cache_lock при использовании SSI-подзапросов.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nin the \"proxy_cache_lock\" directive with SSI subrequests.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.7\" date=\"2014-10-28\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx учитывает при кэшировании строку \"Vary\"\nв заголовке ответа бэкенда.\n</para>\n<para lang=\"en\">\nnow nginx takes into account the \"Vary\"\nheader line in a backend response while caching.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_force_ranges, fastcgi_force_ranges,\nscgi_force_ranges и uwsgi_force_ranges.\n</para>\n<para lang=\"en\">\nthe \"proxy_force_ranges\", \"fastcgi_force_ranges\",\n\"scgi_force_ranges\", and \"uwsgi_force_ranges\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_limit_rate, fastcgi_limit_rate,\nscgi_limit_rate и uwsgi_limit_rate.\n</para>\n<para lang=\"en\">\nthe \"proxy_limit_rate\", \"fastcgi_limit_rate\",\n\"scgi_limit_rate\", and \"uwsgi_limit_rate\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр Vary директив proxy_ignore_headers, fastcgi_ignore_headers,\nscgi_ignore_headers и uwsgi_ignore_headers.\n</para>\n<para lang=\"en\">\nthe \"Vary\" parameter of the \"proxy_ignore_headers\", \"fastcgi_ignore_headers\",\n\"scgi_ignore_headers\", and \"uwsgi_ignore_headers\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпоследняя часть ответа, полученного от бэкенда\nпри небуферизированном проксировании,\nмогла не отправляться клиенту,\nесли использовались директивы gzip или gunzip.\n</para>\n<para lang=\"en\">\nthe last part of a response received from a backend\nwith unbufferred proxy\nmight not be sent to a client\nif \"gzip\" or \"gunzip\" directives were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_cache_revalidate.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the \"proxy_cache_revalidate\" directive.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок.<br/>\nСпасибо Yichun Zhang и Даниилу Бондареву.\n</para>\n<para lang=\"en\">\nin error handling.<br/>\nThanks to Yichun Zhang and Daniil Bondarev.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директивах\nproxy_next_upstream_tries и proxy_next_upstream_timeout.<br/>\nСпасибо Feng Gu.\n</para>\n<para lang=\"en\">\nin the \"proxy_next_upstream_tries\" and \"proxy_next_upstream_timeout\"\ndirectives.<br/>\nThanks to Feng Gu.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не собирался с MinGW-w64 gcc.<br/>\nСпасибо Kouhei Sutou.\n</para>\n<para lang=\"en\">\nnginx/Windows could not be built with MinGW-w64 gcc.<br/>\nThanks to Kouhei Sutou.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.6\" date=\"2014-09-30\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nустаревшая директива limit_zone больше не поддерживается.\n</para>\n<para lang=\"en\">\nthe deprecated \"limit_zone\" directive is not supported anymore.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв директивах limit_conn_zone и limit_req_zone теперь можно использовать\nкомбинации нескольких переменных.\n</para>\n<para lang=\"en\">\nthe \"limit_conn_zone\" and \"limit_req_zone\" directives now can be used\nwith combinations of multiple variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри повторной отправке FastCGI-запроса на бэкенд\nтело запроса могло передаваться неправильно.\n</para>\n<para lang=\"en\">\nrequest body might be transmitted incorrectly\nwhen retrying a FastCGI request to the next upstream server.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв логгировании в syslog.\n</para>\n<para lang=\"en\">\nin logging to syslog.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.5\" date=\"2014-09-16\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании общего для нескольких блоков server\nразделяемого кэша SSL-сессий или общего ключа для шифрования\nTLS session tickets было возможно повторно использовать\nSSL-сессию в контексте другого блока server (CVE-2014-3616).<br/>\nСпасибо Antoine Delignat-Lavaud.\n</para>\n<para lang=\"en\">\nit was possible to reuse SSL sessions in unrelated contexts\nif a shared SSL session cache or the same TLS session ticket key\nwas used for multiple \"server\" blocks (CVE-2014-3616).<br/>\nThanks to Antoine Delignat-Lavaud.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективу stub_status теперь можно указывать без параметров.\n</para>\n<para lang=\"en\">\nnow the \"stub_status\" directive does not require a parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр always директивы add_header.\n</para>\n<para lang=\"en\">\nthe \"always\" parameter of the \"add_header\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы\nproxy_next_upstream_tries, proxy_next_upstream_timeout,\nfastcgi_next_upstream_tries, fastcgi_next_upstream_timeout,\nmemcached_next_upstream_tries, memcached_next_upstream_timeout,\nscgi_next_upstream_tries, scgi_next_upstream_timeout,\nuwsgi_next_upstream_tries и uwsgi_next_upstream_timeout.\n</para>\n<para lang=\"en\">\nthe\n\"proxy_next_upstream_tries\", \"proxy_next_upstream_timeout\",\n\"fastcgi_next_upstream_tries\", \"fastcgi_next_upstream_timeout\",\n\"memcached_next_upstream_tries\", \"memcached_next_upstream_timeout\",\n\"scgi_next_upstream_tries\", \"scgi_next_upstream_timeout\",\n\"uwsgi_next_upstream_tries\", and \"uwsgi_next_upstream_timeout\"\ndirectives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв параметре if директивы access_log.\n</para>\n<para lang=\"en\">\nin the \"if\" parameter of the \"access_log\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_perl_module.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the ngx_http_perl_module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива listen почтового прокси-сервера\nне позволяла указать более двух параметров.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive of the mail proxy module\ndid not allow to specify more than two parameters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива sub_filter не работала\nс заменяемой строкой из одного символа.\n</para>\n<para lang=\"en\">\nthe \"sub_filter\" directive did not work\nwith a string to replace consisting of a single character.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзапросы могли зависать, если использовался resolver\nи в процессе обращения к DNS-серверу происходил таймаут.\n</para>\n<para lang=\"en\">\nrequests might hang if resolver was used\nand a timeout occurred during a DNS request.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module при использовании совместно с AIO.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module when using with AIO.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли с помощью директивы set изменялись переменные\n\"$http_...\", \"$sent_http_...\" или \"$upstream_http_...\".\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"set\" directive was used to change the \"$http_...\",\n\"$sent_http_...\", or \"$upstream_http_...\" variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти.<br/>\nСпасибо Markus Linnala и Feng Gu.\n</para>\n<para lang=\"en\">\nin memory allocation error handling.<br/>\nThanks to Markus Linnala and Feng Gu.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.4\" date=\"2014-08-05\">\n\n<change type=\"security\">\n<para lang=\"ru\">\npipelined-команды не отбрасывались\nпосле команды STARTTLS в SMTP прокси-сервере (CVE-2014-3556);\nошибка появилась в 1.5.6.<br/>\nСпасибо Chris Boulton.\n</para>\n<para lang=\"en\">\npipelined commands were not discarded\nafter STARTTLS command in SMTP proxy (CVE-2014-3556);\nthe bug had appeared in 1.5.6.<br/>\nThanks to Chris Boulton.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nэкранирование символов в URI теперь использует\nшестнадцатеричные цифры в верхнем регистре.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nURI escaping now uses\nuppercase hexadecimal digits.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx можно собрать с BoringSSL и LibreSSL.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nnow nginx can be build with BoringSSL and LibreSSL.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзапросы могли зависать, если использовался resolver\nи DNS-сервер возвращал некорректный ответ;\nошибка появилась в 1.5.8.\n</para>\n<para lang=\"en\">\nrequests might hang if resolver was used\nand a DNS server returned a malformed response;\nthe bug had appeared in 1.5.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $uri могла содержать мусор\nпри возврате ошибок с кодом 400.<br/>\nСпасибо Сергею Боброву.\n</para>\n<para lang=\"en\">\nthe $uri variable might contain garbage\nwhen returning errors with code 400.<br/>\nThanks to Sergey Bobrov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок в директиве proxy_store\nи в модуле ngx_http_dav_module.<br/>\nСпасибо Feng Gu.\n</para>\n<para lang=\"en\">\nin error handling in the \"proxy_store\" directive\nand the ngx_http_dav_module.<br/>\nThanks to Feng Gu.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри логгировании ошибок в syslog мог происходить segmentation fault;\nошибка появилась в 1.7.1.\n</para>\n<para lang=\"en\">\na segmentation fault might occur if logging of errors to syslog was used;\nthe bug had appeared in 1.7.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременные $geoip_latitude, $geoip_longitude, $geoip_dma_code\nи $geoip_area_code могли не работать.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nthe $geoip_latitude, $geoip_longitude, $geoip_dma_code,\nand $geoip_area_code variables might not work.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти.<br/>\nСпасибо Tatsuhiko Kubo и Piotr Sikora.\n</para>\n<para lang=\"en\">\nin memory allocation error handling.<br/>\nThanks to Tatsuhiko Kubo and Piotr Sikora.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.3\" date=\"2014-07-08\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nweak entity tags теперь не удаляются при изменениях ответа,\nа strong entity tags преобразуются в weak.\n</para>\n<para lang=\"en\">\nweak entity tags are now preserved on response modifications,\nand strong ones are changed to weak.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nревалидация элементов кэша теперь, если это возможно,\nиспользует заголовок If-None-Match.\n</para>\n<para lang=\"en\">\ncache revalidation now uses If-None-Match header\nif possible.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_password_file.\n</para>\n<para lang=\"en\">\nthe \"ssl_password_file\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри возврате ответа из кэша\nзаголовок запроса If-None-Match игнорировался,\nесли в ответе не было заголовка Last-Modified.\n</para>\n<para lang=\"en\">\nthe If-None-Match request header line was ignored\nif there was no Last-Modified header\nin a response returned from cache.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсообщения \"peer closed connection in SSL handshake\"\nпри соединении с бэкендами логгировались на уровне info вместо error.\n</para>\n<para lang=\"en\">\n\"peer closed connection in SSL handshake\" messages\nwere logged at \"info\" level instead of \"error\" while connecting to backends.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_dav_module в nginx/Windows.\n</para>\n<para lang=\"en\">\nin the ngx_http_dav_module module in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSPDY-соединения могли неожиданно закрываться,\nесли использовалось кэширование.\n</para>\n<para lang=\"en\">\nSPDY connections might be closed prematurely\nif caching was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.2\" date=\"2014-06-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива hash в блоке upstream.\n</para>\n<para lang=\"en\">\nthe \"hash\" directive inside the \"upstream\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдефрагментация свободных блоков разделяемой памяти.<br/>\nСпасибо Wandenberg Peixoto и Yichun Zhang.\n</para>\n<para lang=\"en\">\ndefragmentation of free shared memory blocks.<br/>\nThanks to Wandenberg Peixoto and Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалось значение access_log по умолчанию;\nошибка появилась в 1.7.0.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the default value of the \"access_log\" directive was used;\nthe bug had appeared in 1.7.0.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзавершающий слэш ошибочно удалялся\nиз последнего параметра директивы try_files.\n</para>\n<para lang=\"en\">\ntrailing slash was mistakenly removed\nfrom the last parameter of the \"try_files\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог не собираться на OS X.\n</para>\n<para lang=\"en\">\nnginx could not be built on OS X in some cases.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.1\" date=\"2014-05-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные \"$upstream_cookie_...\".\n</para>\n<para lang=\"en\">\nthe \"$upstream_cookie_...\" variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_client_fingerprint.\n</para>\n<para lang=\"en\">\nthe $ssl_client_fingerprint variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы error_log и access_log теперь поддерживают логгирование в syslog.\n</para>\n<para lang=\"en\">\nthe \"error_log\" and \"access_log\" directives now support logging to syslog.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпочтовый прокси-сервер теперь логгирует порт клиента при соединении.\n</para>\n<para lang=\"en\">\nthe mail proxy now logs client port on connect.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти при использовании директивы \"ssl_stapling\".<br/>\nСпасибо Filipe da Silva.\n</para>\n<para lang=\"en\">\nmemory leak if the \"ssl_stapling\" directive was used.<br/>\nThanks to Filipe da Silva.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива alias внутри location'а, заданного регулярным выражением,\nработала неправильно, если использовались директивы if или limit_except.\n</para>\n<para lang=\"en\">\nthe \"alias\" directive used inside a location given by a regular expression\nworked incorrectly if the \"if\" or \"limit_except\" directives were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива charset не ставила кодировку для сжатых ответов бэкендов.\n</para>\n<para lang=\"en\">\nthe \"charset\" directive did not set a charset to encoded backend responses.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_pass без URI могла использовать оригинальный запрос\nпосле установки переменной $args.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\na \"proxy_pass\" directive without URI part might use original request\nafter the $args variable was set.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв работе параметра none директивы smtp_auth;\nошибка появилась в 1.5.6.<br/>\nСпасибо Святославу Никольскому.\n</para>\n<para lang=\"en\">\nin the \"none\" parameter in the \"smtp_auth\" directive;\nthe bug had appeared in 1.5.6.<br/>\nThanks to Svyatoslav Nikolsky.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри совместном использовании sub_filter и SSI\nответы могли передаваться неверно.\n</para>\n<para lang=\"en\">\nif sub_filter and SSI were used together,\nthen responses might be transferred incorrectly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --with-file-aio на Linux/aarch64.\n</para>\n<para lang=\"en\">\nnginx could not be built with the --with-file-aio option on Linux/aarch64.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.7.0\" date=\"2014-04-24\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпроверка SSL-сертификатов бэкендов.\n</para>\n<para lang=\"en\">\nbackend SSL certificate verification.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка SNI при работе с бэкендами по SSL.\n</para>\n<para lang=\"en\">\nsupport for SNI while working with SSL backends.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_server_name.\n</para>\n<para lang=\"en\">\nthe $ssl_server_name variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр if директивы access_log.\n</para>\n<para lang=\"en\">\nthe \"if\" parameter of the \"access_log\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.13\" date=\"2014-04-08\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nулучшена обработка хэш-таблиц;\nв директивах variables_hash_max_size и types_hash_bucket_size\nзначения по умолчанию изменены на 1024 и 64 соответственно.\n</para>\n<para lang=\"en\">\nimproved hash table handling;\nthe default values of the \"variables_hash_max_size\" and\n\"types_hash_bucket_size\" were changed to 1024 and 64 respectively.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_mp4_module теперь понимает аргумент end.\n</para>\n<para lang=\"en\">\nthe ngx_http_mp4_module now supports the \"end\" argument.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка byte ranges модулем ngx_http_mp4_module и при сохранении\nответов в кэш.\n</para>\n<para lang=\"en\">\nbyte ranges support in the ngx_http_mp4_module and while saving responses\nto cache.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx не пишет в лог сообщения \"ngx_slab_alloc() failed: no memory\"\nпри использовании разделяемой памяти в ssl_session_cache\nи в модуле ngx_http_limit_req_module.\n</para>\n<para lang=\"en\">\nalerts \"ngx_slab_alloc() failed: no memory\" no longer logged\nwhen using shared memory in the \"ssl_session_cache\" directive\nand in the ngx_http_limit_req_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива underscores_in_headers\nне разрешала подчёркивание в первом символе заголовка.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe \"underscores_in_headers\" directive\ndid not allow underscore as a first character of a header.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\ncache manager мог нагружать процессор при выходе в nginx/Windows.\n</para>\n<para lang=\"en\">\ncache manager might hog CPU on exit in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании ssl_session_cache с параметром shared\nрабочий процесс nginx/Windows завершался аварийно.\n</para>\n<para lang=\"en\">\nnginx/Windows terminated abnormally\nif the \"ssl_session_cache\" directive was used with the \"shared\" parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.12\" date=\"2014-03-18\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри обработке специально созданного запроса модулем ngx_http_spdy_module\nмогло происходить переполнение буфера в рабочем процессе,\nчто потенциально могло приводить к выполнению произвольного кода\n(CVE-2014-0133).<br/>\nСпасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel\nSadosky, Buenos Aires, Argentina.\n</para>\n<para lang=\"en\">\na heap memory buffer overflow might occur in a worker process\nwhile handling a specially crafted request by ngx_http_spdy_module,\npotentially resulting in arbitrary code execution\n(CVE-2014-0133).<br/>\nThanks to Lucas Molas, researcher at Programa STIC, Fundación Dr. Manuel\nSadosky, Buenos Aires, Argentina.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр proxy_protocol в директивах listen и real_ip_header,\nпеременная $proxy_protocol_addr.\n</para>\n<para lang=\"en\">\nthe \"proxy_protocol\" parameters of the \"listen\" and \"real_ip_header\" directives,\nthe $proxy_protocol_addr variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве fastcgi_next_upstream.<br/>\nСпасибо Lucas Molas.\n</para>\n<para lang=\"en\">\nin the \"fastcgi_next_upstream\" directive.<br/>\nThanks to Lucas Molas.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.11\" date=\"2014-03-04\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри обработке специально созданного запроса модулем ngx_http_spdy_module\nна 32-битных платформах могла повреждаться память рабочего процесса,\nчто потенциально могло приводить к выполнению произвольного кода\n(CVE-2014-0088);\nошибка появилась в 1.5.10.<br/>\nСпасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel\nSadosky, Buenos Aires, Argentina.\n</para>\n<para lang=\"en\">\nmemory corruption might occur in a worker process on 32-bit platforms\nwhile handling a specially crafted request by ngx_http_spdy_module,\npotentially resulting in arbitrary code execution (CVE-2014-0088);\nthe bug had appeared in 1.5.10.<br/>\nThanks to Lucas Molas, researcher at Programa STIC, Fundación Dr. Manuel\nSadosky, Buenos Aires, Argentina.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_session_reused.\n</para>\n<para lang=\"en\">\nthe $ssl_session_reused variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива client_max_body_size могла не работать\nпри чтении тела запроса с использованием chunked transfer encoding;\nошибка появилась в 1.3.9.<br/>\nСпасибо Lucas Molas.\n</para>\n<para lang=\"en\">\nthe \"client_max_body_size\" directive might not work\nwhen reading a request body using chunked transfer encoding;\nthe bug had appeared in 1.3.9.<br/>\nThanks to Lucas Molas.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании WebSocket-соединений\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhen proxying WebSocket connections.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался модуль ngx_http_spdy_module на 32-битных платформах;\nошибка появилась в 1.5.10.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the ngx_http_spdy_module was used on 32-bit platforms;\nthe bug had appeared in 1.5.10.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзначение переменной $upstream_status могло быть неверным,\nесли использовались директивы proxy_cache_use_stale\nили proxy_cache_revalidate.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe $upstream_status variable might contain wrong data\nif the \"proxy_cache_use_stale\" or \"proxy_cache_revalidate\" directives\nwere used.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли ошибки с кодом 400 с помощью директивы error_page\nперенаправлялись в именованный location.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif errors with code 400 were redirected to a named location\nusing the \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не собирался с Visual Studio 2013.\n</para>\n<para lang=\"en\">\nnginx/Windows could not be built with Visual Studio 2013.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.10\" date=\"2014-02-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_spdy_module теперь использует протокол SPDY 3.1.<br/>\nСпасибо Automattic и MaxCDN за спонсирование разработки.\n</para>\n<para lang=\"en\">\nthe ngx_http_spdy_module now uses SPDY 3.1 protocol.<br/>\nThanks to Automattic and MaxCDN for sponsoring this work.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_mp4_module теперь пропускает дорожки,\nимеющие меньшую длину, чем запрошенная перемотка.\n</para>\n<para lang=\"en\">\nthe ngx_http_mp4_module now skips tracks\ntoo short for a seek requested.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли переменная $ssl_session_id использовалась при логгировании;\nошибка появилась в 1.5.9.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the $ssl_session_id variable was used in logs;\nthe bug had appeared in 1.5.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременные $date_local и $date_gmt использовали неверный формат\nвне модуля ngx_http_ssi_filter_module.\n</para>\n<para lang=\"en\">\nthe $date_local and $date_gmt variables used wrong format\noutside of the ngx_http_ssi_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nклиентские соединения могли сразу закрываться,\nесли использовался отложенный accept;\nошибка появилась в 1.3.15.\n</para>\n<para lang=\"en\">\nclient connections might be immediately closed\nif deferred accept was used;\nthe bug had appeared in 1.3.15.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсообщения \"getsockopt(TCP_FASTOPEN) ... failed\" записывались в лог\nв процессе обновления исполняемого файла на Linux;\nошибка появилась в 1.5.8.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nalerts \"getsockopt(TCP_FASTOPEN) ... failed\" appeared in logs\nduring binary upgrade on Linux;\nthe bug had appeared in 1.5.8.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.9\" date=\"2014-01-22\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь в заголовке X-Accel-Redirect nginx ожидает закодированный URI.\n</para>\n<para lang=\"en\">\nnow nginx expects escaped URIs in \"X-Accel-Redirect\" headers.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_buffer_size.\n</para>\n<para lang=\"en\">\nthe \"ssl_buffer_size\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу limit_rate теперь можно использовать для\nограничения скорости передачи ответов клиенту в SPDY-соединениях.\n</para>\n<para lang=\"en\">\nthe \"limit_rate\" directive can now be used to\nrate limit responses sent in SPDY connections.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива spdy_chunk_size.\n</para>\n<para lang=\"en\">\nthe \"spdy_chunk_size\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_session_tickets.<br/>\nСпасибо Dirkjan Bussink.\n</para>\n<para lang=\"en\">\nthe \"ssl_session_tickets\" directive.<br/>\nThanks to Dirkjan Bussink.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $ssl_session_id содержала всю сессию в сериализованном виде\nвместо её идентификатора.<br/>\nСпасибо Ivan Ristić.\n</para>\n<para lang=\"en\">\nthe $ssl_session_id variable contained full session serialized\ninstead of just a session id.<br/>\nThanks to Ivan Ristić.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx неправильно обрабатывал закодированный символ \"?\" в команде SSI include.\n</para>\n<para lang=\"en\">\nnginx incorrectly handled escaped \"?\" character in the \"include\" SSI command.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module не раскодировал целевой URI при\nобработке методов COPY и MOVE.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module did not unescape destination URI\nof the COPY and MOVE methods.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nresolver не понимал доменные имена с точкой в конце.\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nresolver did not understand domain names with a trailing dot.\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании в логах могли появляться сообщения \"zero size buf in output\";\nошибка появилась в 1.3.9.\n</para>\n<para lang=\"en\">\nalerts \"zero size buf in output\" might appear in logs while proxying;\nthe bug had appeared in 1.3.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался модуль ngx_http_spdy_module.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the ngx_http_spdy_module was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании методов обработки соединений select, poll и /dev/poll\nпроксируемые WebSocket-соединения могли зависать сразу после открытия.\n</para>\n<para lang=\"en\">\nproxied WebSocket connections might hang right after handshake\nif the select, poll, or /dev/poll methods were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива xclient почтового прокси-сервера\nнекорректно передавала IPv6-адреса.\n</para>\n<para lang=\"en\">\nthe \"xclient\" directive of the mail proxy module\nincorrectly handled IPv6 client addresses.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.8\" date=\"2013-12-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь resolver поддерживает IPv6.\n</para>\n<para lang=\"en\">\nIPv6 support in resolver.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива listen поддерживает параметр fastopen.<br/>\nСпасибо Mathew Rodley.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive supports the \"fastopen\" parameter.<br/>\nThanks to Mathew Rodley.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка SSL в модуле ngx_http_uwsgi_module.<br/>\nСпасибо Roberto De Ioris.\n</para>\n<para lang=\"en\">\nSSL support in the ngx_http_uwsgi_module.<br/>\nThanks to Roberto De Ioris.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nскрипты подсветки синтаксиса для vim добавлены в contrib.<br/>\nСпасибо Evan Miller.\n</para>\n<para lang=\"en\">\nvim syntax highlighting scripts were added to contrib.<br/>\nThanks to Evan Miller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри чтении тела запроса с использованием chunked transfer encoding\nпо SSL-соединению мог произойти таймаут.\n</para>\n<para lang=\"en\">\na timeout might occur while reading client request body\nin an SSL connection using chunked transfer encoding.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива master_process работала неправильно в nginx/Windows.\n</para>\n<para lang=\"en\">\nthe \"master_process\" directive did not work correctly in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр setfib директивы listen мог не работать.\n</para>\n<para lang=\"en\">\nthe \"setfib\" parameter of the \"listen\" directive might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.7\" date=\"2013-11-19\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nсимвол, следующий за незакодированным пробелом в строке запроса,\nобрабатывался неправильно (CVE-2013-4547);\nошибка появилась в 0.8.41.<br/>\nСпасибо Ivan Fratric из Google Security Team.\n</para>\n<para lang=\"en\">\na character following an unescaped space in a request line\nwas handled incorrectly (CVE-2013-4547);\nthe bug had appeared in 0.8.41.<br/>\nThanks to Ivan Fratric of the Google Security Team.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nуровень логгирования ошибок auth_basic об отсутствии пароля\nпонижен с уровня error до info.\n</para>\n<para lang=\"en\">\na logging level of auth_basic errors about no user/password provided\nhas been lowered from \"error\" to \"info\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_cache_revalidate, fastcgi_cache_revalidate,\nscgi_cache_revalidate и uwsgi_cache_revalidate.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_revalidate\", \"fastcgi_cache_revalidate\",\n\"scgi_cache_revalidate\", and \"uwsgi_cache_revalidate\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_session_ticket_key.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe \"ssl_session_ticket_key\" directive.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"add_header Cache-Control ''\"\nдобавляла строку заголовка ответа \"Cache-Control\" с пустым значением.\n</para>\n<para lang=\"en\">\nthe directive \"add_header Cache-Control ''\"\nadded a \"Cache-Control\" response header line with an empty value.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"satisfy any\" могла вернуть ошибку 403 вместо 401\nпри использовании директив auth_request и auth_basic.<br/>\nСпасибо Jan Marc Hoffmann.\n</para>\n<para lang=\"en\">\nthe \"satisfy any\" directive might return 403 error instead of 401\nif auth_request and auth_basic directives were used.<br/>\nThanks to Jan Marc Hoffmann.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметры accept_filter и deferred директивы listen игнорировались\nдля listen-сокетов, создаваемых в процессе обновления исполняемого файла.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe \"accept_filter\" and \"deferred\" parameters of the \"listen\" directive\nwere ignored for listen sockets created during binary upgrade.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nчасть данных, полученных от бэкенда при небуферизированном проксировании,\nмогла не отправляться клиенту сразу,\nесли использовались директивы gzip или gunzip.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nsome data received from a backend with unbufferred proxy\nmight not be sent to a client immediately\nif \"gzip\" or \"gunzip\" directives were used.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок в модуле ngx_http_gunzip_filter_module.\n</para>\n<para lang=\"en\">\nin error handling in ngx_http_gunzip_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы могли зависать,\nесли использовался модуль ngx_http_spdy_module\nи директива auth_request.\n</para>\n<para lang=\"en\">\nresponses might hang\nif the ngx_http_spdy_module was used\nwith the \"auth_request\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти в nginx/Windows.\n</para>\n<para lang=\"en\">\nmemory leak in nginx/Windows.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.6\" date=\"2013-10-01\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_buffering.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_buffering\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_ssl_protocols и proxy_ssl_ciphers.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe \"proxy_ssl_protocols\" and \"proxy_ssl_ciphers\" directives.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nоптимизация SSL handshake при использовании длинных цепочек сертификатов.\n</para>\n<para lang=\"en\">\noptimization of SSL handshakes when using long certificate chains.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпочтовый прокси-сервер поддерживает SMTP pipelining.\n</para>\n<para lang=\"en\">\nthe mail proxy supports SMTP pipelining.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_auth_basic_module\nпри использовании метода шифрования паролей \"$apr1$\".<br/>\nСпасибо Markus Linnala.\n</para>\n<para lang=\"en\">\nin the ngx_http_auth_basic_module\nwhen using \"$apr1$\" password encryption method.<br/>\nThanks to Markus Linnala.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна MacOSX, Cygwin и nginx/Windows\nдля обработки запроса мог использоваться неверный location,\nесли для задания location'ов использовались символы разных регистров.\n</para>\n<para lang=\"en\">\nin MacOSX, Cygwin, and nginx/Windows\nincorrect location might be used to process a request\nif locations were given using characters in different cases.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nавтоматическое перенаправление с добавлением завершающего слэша\nдля проксированных location'ов могло не работать.\n</para>\n<para lang=\"en\">\nautomatic redirect with appended trailing slash\nfor proxied locations might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв почтовом прокси-сервере.\n</para>\n<para lang=\"en\">\nin the mail proxy server.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.5\" date=\"2013-09-17\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx по умолчанию использует HTTP/1.0,\nесли точно определить протокол не удалось.\n</para>\n<para lang=\"en\">\nnow nginx assumes HTTP/1.0 by default\nif it is not able to detect protocol reliably.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива disable_symlinks теперь использует O_PATH на Linux.\n</para>\n<para lang=\"en\">\nthe \"disable_symlinks\" directive now uses O_PATH on Linux.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдля определения того, что клиент закрыл соединение,\nпри использовании метода epoll\nтеперь используются события EPOLLRDHUP.\n</para>\n<para lang=\"en\">\nnow nginx uses EPOLLRDHUP events\nto detect premature connection close by clients\nif the \"epoll\" method is used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве valid_referers при использовании параметра server_names.\n</para>\n<para lang=\"en\">\nin the \"valid_referers\" directive if the \"server_names\" parameter was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $request_time не работала в nginx/Windows.\n</para>\n<para lang=\"en\">\nthe $request_time variable did not work in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве image_filter.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\nin the \"image_filter\" directive.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с OpenSSL 1.0.1f.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nOpenSSL 1.0.1f compatibility.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n\n</changes>\n\n\n<changes ver=\"1.5.4\" date=\"2013-08-27\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nMIME-тип для расширения js изменён на \"application/javascript\";\nзначение по умолчанию директивы charset_types изменено соответственно.\n</para>\n<para lang=\"en\">\nthe \"js\" extension MIME type has been changed to \"application/javascript\";\ndefault value of the \"charset_types\" directive was changed accordingly.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директива image_filter с параметром size\nвозвращает ответ с MIME-типом \"application/json\".\n</para>\n<para lang=\"en\">\nnow the \"image_filter\" directive with the \"size\" parameter\nreturns responses with the \"application/json\" MIME type.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_auth_request_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_auth_request_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна старте или во время переконфигурации мог произойти segmentation fault,\nесли использовалась директива try_files с пустым параметром.\n</para>\n<para lang=\"en\">\na segmentation fault might occur on start or during reconfiguration\nif the \"try_files\" directive was used with an empty parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти при использовании в директивах root и auth_basic_user_file\nотносительных путей, заданных с помощью переменных.\n</para>\n<para lang=\"en\">\nmemory leak if relative paths were specified using variables\nin the \"root\" or \"auth_basic_user_file\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива valid_referers неправильно выполняла регулярные выражения,\nесли заголовок Referer начинался с \"https://\".<br/>\nСпасибо Liangbin Li.\n</para>\n<para lang=\"en\">\nthe \"valid_referers\" directive incorrectly executed regular expressions\nif a \"Referer\" header started with \"https://\".<br/>\nThanks to Liangbin Li.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы могли зависать, если использовались подзапросы и при обработке подзапроса\nпроисходила ошибка во время SSL handshake с бэкендом.<br/>\nСпасибо Aviram Cohen.\n</para>\n<para lang=\"en\">\nresponses might hang if subrequests were used\nand an SSL handshake error happened during subrequest processing.<br/>\nThanks to Aviram Cohen.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_autoindex_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_autoindex_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.3\" date=\"2013-07-30\">\n\n<change>\n<para lang=\"ru\">\nИзменение во внутреннем API:\nтеперь при небуферизированной работе с бэкендами\nu->length по умолчанию устанавливается в -1.\n</para>\n<para lang=\"en\">\nChange in internal API:\nnow u->length defaults to -1\nif working with backends in unbuffered mode.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при получении неполного ответа от бэкенда\nnginx отправляет полученную часть ответа,\nпосле чего закрывает соединение с клиентом.\n</para>\n<para lang=\"en\">\nnow after receiving an incomplete response from a backend server\nnginx tries to send an available part of the response to a client,\nand then closes client connection.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался модуль ngx_http_spdy_module\nи директива client_body_in_file_only.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the ngx_http_spdy_module was used\nwith the \"client_body_in_file_only\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр so_keepalive директивы listen\nмог работать некорректно на DragonFlyBSD.<br/>\nСпасибо Sepherosa Ziehau.\n</para>\n<para lang=\"en\">\nthe \"so_keepalive\" parameter of the \"listen\" directive\nmight be handled incorrectly on DragonFlyBSD.<br/>\nThanks to Sepherosa Ziehau.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_xslt_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_xslt_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_sub_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_sub_filter_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.2\" date=\"2013-07-02\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь можно использовать несколько директив error_log.\n</para>\n<para lang=\"en\">\nnow several \"error_log\" directives can be used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод $r->header_in() встроенного перла не возвращал значения строк\n\"Cookie\" и \"X-Forwarded-For\" из заголовка запроса;\nошибка появилась в 1.3.14.\n</para>\n<para lang=\"en\">\nthe $r->header_in() embedded perl method did not return value of the\n\"Cookie\" and \"X-Forwarded-For\" request header lines;\nthe bug had appeared in 1.3.14.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_spdy_module.<br/>\nСпасибо Jim Radford.\n</para>\n<para lang=\"en\">\nin the ngx_http_spdy_module.<br/>\nThanks to Jim Radford.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Linux при использовании x32 ABI.<br/>\nСпасибо Сергею Иванцову.\n</para>\n<para lang=\"en\">\nnginx could not be built on Linux with x32 ABI.<br/>\nThanks to Serguei Ivantsov.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.1\" date=\"2013-06-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssi_last_modified, sub_filter_last_modified и\nxslt_last_modified.<br/>\nСпасибо Алексею Колпакову.\n</para>\n<para lang=\"en\">\nthe \"ssi_last_modified\", \"sub_filter_last_modified\", and\n\"xslt_last_modified\" directives.<br/>\nThanks to Alexey Kolpakov.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр http_403 в директивах proxy_next_upstream, fastcgi_next_upstream,\nscgi_next_upstream и uwsgi_next_upstream.\n</para>\n<para lang=\"en\">\nthe \"http_403\" parameter of the \"proxy_next_upstream\", \"fastcgi_next_upstream\",\n\"scgi_next_upstream\", and \"uwsgi_next_upstream\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы allow и deny теперь поддерживают unix domain сокеты.\n</para>\n<para lang=\"en\">\nthe \"allow\" and \"deny\" directives now support unix domain sockets.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с модулем ngx_mail_ssl_module,\nно без модуля ngx_http_ssl_module;\nошибка появилась в 1.3.14.\n</para>\n<para lang=\"en\">\nnginx could not be built with the ngx_mail_ssl_module,\nbut without ngx_http_ssl_module;\nthe bug had appeared in 1.3.14.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_set_body.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\nin the \"proxy_set_body\" directive.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве lingering_time.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\nin the \"lingering_time\" directive.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр fail_timeout директивы server\nв блоке upstream мог не работать,\nесли использовался параметр max_fails;\nошибка появилась в 1.3.0.\n</para>\n<para lang=\"en\">\nthe \"fail_timeout\" parameter of the \"server\" directive\nin the \"upstream\" context might not work\nif \"max_fails\" parameter was used;\nthe bug had appeared in 1.3.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива ssl_stapling.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"ssl_stapling\" directive was used.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв почтовом прокси-сервере.<br/>\nСпасибо Filipe Da Silva.\n</para>\n<para lang=\"en\">\nin the mail proxy server.<br/>\nThanks to Filipe Da Silva.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows мог перестать принимать соединения,\nесли использовалось несколько рабочих процессов.\n</para>\n<para lang=\"en\">\nnginx/Windows might stop accepting connections\nif several worker processes were used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.5.0\" date=\"2013-05-07\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри обработке специально созданного запроса\nмог перезаписываться стек рабочего процесса,\nчто могло приводить к выполнению произвольного кода (CVE-2013-2028);\nошибка появилась в 1.3.9.<br/>\nСпасибо Greg MacManus, iSIGHT Partners Labs.\n</para>\n<para lang=\"en\">\na stack-based buffer overflow might occur in a worker process\nwhile handling a specially crafted request,\npotentially resulting in arbitrary code execution (CVE-2013-2028);\nthe bug had appeared in 1.3.9.<br/>\nThanks to Greg MacManus, iSIGHT Partners Labs.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.4.0\" date=\"2013-04-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с модулем ngx_http_perl_module,\nесли использовался параметр --with-openssl;\nошибка появилась в 1.3.16.\n</para>\n<para lang=\"en\">\nnginx could not be built with the ngx_http_perl_module\nif the --with-openssl option was used;\nthe bug had appeared in 1.3.16.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв работе с телом запроса из модуля ngx_http_perl_module;\nошибка появилась в 1.3.9.\n</para>\n<para lang=\"en\">\nin a request body handling in the ngx_http_perl_module;\nthe bug had appeared in 1.3.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.16\" date=\"2013-04-16\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовались подзапросы;\nошибка появилась в 1.3.9.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif subrequests were used;\nthe bug had appeared in 1.3.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива tcp_nodelay вызывала ошибку\nпри проксировании WebSocket-соединений в unix domain сокет.\n</para>\n<para lang=\"en\">\nthe \"tcp_nodelay\" directive caused an error\nif a WebSocket connection was proxied into a unix domain socket.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $upstream_response_length возвращала значение \"0\",\nесли не использовалась буферизация.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe $upstream_response_length variable has an incorrect value \"0\"\nif buffering was not used.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв методах обработки соединений eventport и /dev/poll.\n</para>\n<para lang=\"en\">\nin the eventport and /dev/poll methods.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.15\" date=\"2013-03-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nоткрытие и закрытие соединения без отправки в нём каких-либо данных\nбольше не записывается в access_log с кодом ошибки 400.\n</para>\n<para lang=\"en\">\nopening and closing a connection without sending any data in it\nis no longer logged to access_log with error code 400.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_spdy_module.<br/>\nСпасибо Automattic за спонсирование разработки.\n</para>\n<para lang=\"en\">\nthe ngx_http_spdy_module.<br/>\nThanks to Automattic for sponsoring this work.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы limit_req_status и limit_conn_status.<br/>\nСпасибо Nick Marden.\n</para>\n<para lang=\"en\">\nthe \"limit_req_status\" and \"limit_conn_status\" directives.<br/>\nThanks to Nick Marden.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива image_filter_interlace.<br/>\nСпасибо Ивану Боброву.\n</para>\n<para lang=\"en\">\nthe \"image_filter_interlace\" directive.<br/>\nThanks to Ian Babrou.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $connections_waiting в модуле ngx_http_stub_status_module.\n</para>\n<para lang=\"en\">\n$connections_waiting variable in the ngx_http_stub_status_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь почтовый прокси-сервер поддерживает IPv6-бэкенды.\n</para>\n<para lang=\"en\">\nthe mail proxy module now supports IPv6 backends.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри повторной отправке запроса на бэкенд\nтело запроса могло передаваться неправильно;\nошибка появилась в 1.3.9.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nrequest body might be transmitted incorrectly\nwhen retrying a request to the next upstream server;\nthe bug had appeared in 1.3.9.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве client_body_in_file_only;\nошибка появилась в 1.3.9.\n</para>\n<para lang=\"en\">\nin the \"client_body_in_file_only\" directive;\nthe bug had appeared in 1.3.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы могли зависать,\nесли использовались подзапросы\nи при обработке подзапроса происходила DNS-ошибка.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\nresponses might hang\nif subrequests were used\nand a DNS error happened during subrequest processing.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв процедуре учёта использования бэкендов.\n</para>\n<para lang=\"en\">\nin backend usage accounting.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.14\" date=\"2013-03-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $connections_active, $connections_reading и $connections_writing\nв модуле ngx_http_stub_status_module.\n</para>\n<para lang=\"en\">\n$connections_active, $connections_reading, and $connections_writing variables\nin the ngx_http_stub_status_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка WebSocket-соединений\nв модулях ngx_http_uwsgi_module и ngx_http_scgi_module.\n</para>\n<para lang=\"en\">\nsupport of WebSocket connections\nin the ngx_http_uwsgi_module and ngx_http_scgi_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке виртуальных серверов при использовании SNI.\n</para>\n<para lang=\"en\">\nin virtual servers handling with SNI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы \"ssl_session_cache shared\"\nновые сессии могли не сохраняться,\nесли заканчивалось место в разделяемой памяти.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nnew sessions were not always stored\nif the \"ssl_session_cache shared\" directive was used\nand there was no free space in shared memory.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнесколько заголовков X-Forwarded-For обрабатывались неправильно.<br/>\nСпасибо Neal Poole за спонсирование разработки.\n</para>\n<para lang=\"en\">\nmultiple X-Forwarded-For headers were handled incorrectly.<br/>\nThanks to Neal Poole for sponsoring this work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.<br/>\nСпасибо Gernot Vormayr.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.<br/>\nThanks to Gernot Vormayr.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.13\" date=\"2013-02-19\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь для сборки по умолчанию используется компилятор с именем \"cc\".\n</para>\n<para lang=\"en\">\na compiler with name \"cc\" is now used by default.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка проксирования WebSocket-соединений.<br/>\nСпасибо Apcera и CloudBees за спонсирование разработки.\n</para>\n<para lang=\"en\">\nsupport for proxying of WebSocket connections.<br/>\nThanks to Apcera and CloudBees for sponsoring this work.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива auth_basic_user_file поддерживает шифрование паролей\nметодом \"{SHA}\".<br/>\nСпасибо Louis Opter.\n</para>\n<para lang=\"en\">\nthe \"auth_basic_user_file\" directive supports \"{SHA}\"\npassword encryption method.<br/>\nThanks to Louis Opter.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.12\" date=\"2013-02-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_bind, fastcgi_bind, memcached_bind, scgi_bind и uwsgi_bind\nподдерживают переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"proxy_bind\", \"fastcgi_bind\", \"memcached_bind\",\n\"scgi_bind\", and \"uwsgi_bind\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $pipe, $request_length, $time_iso8601 и $time_local\nтеперь можно использовать не только в директиве log_format.<br/>\nСпасибо Kiril Kalchev.\n</para>\n<para lang=\"en\">\nthe $pipe, $request_length, $time_iso8601, and $time_local variables\ncan now be used not only in the \"log_format\" directive.\nThanks to Kiril Kalchev.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка IPv6 в модуле ngx_http_geoip_module.<br/>\nСпасибо Gregor Kališnik.\n</para>\n<para lang=\"en\">\nIPv6 support in the ngx_http_geoip_module.<br/>\nThanks to Gregor Kališnik.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_method работала неверно, если была указана на уровне http.\n</para>\n<para lang=\"en\">\nin the \"proxy_method\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался resolver и метод poll.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif resolver was used with the poll method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог нагружать процессор во время SSL handshake с бэкендом\nпри использовании методов обработки соединений select, poll и /dev/poll.\n</para>\n<para lang=\"en\">\nnginx might hog CPU during SSL handshake with a backend\nif the select, poll, or /dev/poll methods were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка \"[crit] SSL_write() failed (SSL:)\".\n</para>\n<para lang=\"en\">\nthe \"[crit] SSL_write() failed (SSL:)\" error.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве client_body_in_file_only;\nошибка появилась в 1.3.9.\n</para>\n<para lang=\"en\">\nin the \"client_body_in_file_only\" directive;\nthe bug had appeared in 1.3.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве fastcgi_keep_conn.\n</para>\n<para lang=\"en\">\nin the \"fastcgi_keep_conn\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.11\" date=\"2013-01-10\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри записи в лог мог происходить segmentation fault;\nошибка появилась в 1.3.10.\n</para>\n<para lang=\"en\">\na segmentation fault might occur if logging was used;\nthe bug had appeared in 1.3.10.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_pass не работала с IP-адресами\nбез явного указания порта;\nошибка появилась в 1.3.10.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\" directive did not work with IP addresses\nwithout port specified;\nthe bug had appeared in 1.3.10.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна старте или во время переконфигурации происходил segmentation fault,\nесли директива keepalive была указана несколько раз\nв одном блоке upstream.\n</para>\n<para lang=\"en\">\na segmentation fault occurred on start or during reconfiguration\nif the \"keepalive\" directive was specified more than once\nin a single upstream block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр default директивы geo не определял значение по умолчанию\nдля IPv6-адресов.\n</para>\n<para lang=\"en\">\nparameter \"default\" of the \"geo\" directive did not set default value\nfor IPv6 addresses.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.10\" date=\"2012-12-25\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдля указанных в конфигурационном файле доменных имён теперь\nиспользуются не только IPv4, но и IPv6 адреса.\n</para>\n<para lang=\"en\">\ndomain names specified in configuration file\nare now resolved to IPv6 addresses as well as IPv4 ones.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при использовании директивы include с маской на Unix-системах\nвключаемые файлы сортируются в алфавитном порядке.\n</para>\n<para lang=\"en\">\nnow if the \"include\" directive with mask is used on Unix systems,\nincluded files are sorted in alphabetical order.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива add_header добавляет строки в ответы с кодом 201.\n</para>\n<para lang=\"en\">\nthe \"add_header\" directive adds headers to 201 responses.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива geo теперь поддерживает IPv6 адреса в формате CIDR.\n</para>\n<para lang=\"en\">\nthe \"geo\" directive now supports IPv6 addresses in CIDR notation.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры flush и gzip в директиве access_log.\n</para>\n<para lang=\"en\">\nthe \"flush\" and \"gzip\" parameters of the \"access_log\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива auth_basic поддерживает переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"auth_basic\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx в некоторых случаях не собирался с модулем ngx_http_perl_module.\n</para>\n<para lang=\"en\">\nnginx could not be built with the ngx_http_perl_module in some cases.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался модуль ngx_http_xslt_module.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the ngx_http_xslt_module was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог не собираться на MacOSX.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nnginx could not be built on MacOSX in some cases.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы limit_rate с большими значениями скорости\nна 32-битных системах ответ мог возвращаться не целиком.<br/>\nСпасибо Алексею Антропову.\n</para>\n<para lang=\"en\">\nthe \"limit_rate\" directive with high rates\nmight result in truncated responses on 32-bit platforms.<br/>\nThanks to Alexey Antropov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива if.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"if\" directive was used.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответ \"100 Continue\" выдавался\nвместе с ответом \"413 Request Entity Too Large\".\n</para>\n<para lang=\"en\">\na \"100 Continue\" response was issued\nwith \"413 Request Entity Too Large\" responses.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы image_filter, image_filter_jpeg_quality и image_filter_sharpen\nмогли наследоваться некорректно.<br/>\nСпасибо Ивану Боброву.\n</para>\n<para lang=\"en\">\nthe \"image_filter\", \"image_filter_jpeg_quality\"\nand \"image_filter_sharpen\" directives\nmight be inherited incorrectly.<br/>\nThanks to Ian Babrou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы auth_basic под Linux\nмогли возникать ошибки \"crypt_r() failed\".\n</para>\n<para lang=\"en\">\n\"crypt_r() failed\" errors might appear\nif the \"auth_basic\" directive was used on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке backup-серверов.<br/>\nСпасибо Thomas Chen.\n</para>\n<para lang=\"en\">\nin backup servers handling.<br/>\nThanks to Thomas Chen.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании HEAD-запросов мог возвращаться некорректный ответ,\nесли использовалась директива gzip.\n</para>\n<para lang=\"en\">\nproxied HEAD requests might return incorrect response\nif the \"gzip\" directive was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.9\" date=\"2012-11-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка chunked transfer encoding при получении тела запроса.\n</para>\n<para lang=\"en\">\nsupport for chunked transfer encoding while reading client request body.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $request_time и $msec\nтеперь можно использовать не только в директиве log_format.\n</para>\n<para lang=\"en\">\nthe $request_time and $msec variables\ncan now be used not only in the \"log_format\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\ncache manager и cache loader могли не запускаться,\nесли использовалось более 512 listen-сокетов.\n</para>\n<para lang=\"en\">\ncache manager and cache loader processes might not be able to start\nif more than 512 listen sockets were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_dav_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_dav_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.8\" date=\"2012-10-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр optional_no_ca директивы ssl_verify_client.<br/>\nСпасибо Михаилу Казанцеву и Eric O'Connor.\n</para>\n<para lang=\"en\">\nthe \"optional_no_ca\" parameter of the \"ssl_verify_client\" directive.<br/>\nThanks to Mike Kazantsev and Eric O'Connor.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $bytes_sent, $connection и $connection_requests\nтеперь можно использовать не только в директиве log_format.<br/>\nСпасибо Benjamin Grössing.\n</para>\n<para lang=\"en\">\nthe $bytes_sent, $connection, and $connection_requests variables\ncan now be used not only in the \"log_format\" directive.<br/>\nThanks to Benjamin Grössing.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр auto директивы worker_processes.\n</para>\n<para lang=\"en\">\nthe \"auto\" parameter of the \"worker_processes\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсообщения \"cache file ... has md5 collision\".\n</para>\n<para lang=\"en\">\n\"cache file ... has md5 collision\" alert.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_gunzip_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_gunzip_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве ssl_stapling.\n</para>\n<para lang=\"en\">\nin the \"ssl_stapling\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.7\" date=\"2012-10-02\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка OCSP stapling.<br/>\nСпасибо Comodo, DigiCert и GlobalSign за спонсирование разработки.\n</para>\n<para lang=\"en\">\nOCSP stapling support.<br/>\nThanks to Comodo, DigiCert and GlobalSign for sponsoring this work.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_trusted_certificate.\n</para>\n<para lang=\"en\">\nthe \"ssl_trusted_certificate\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь resolver случайным образом меняет порядок\nвозвращаемых закэшированных адресов.<br/>\nСпасибо Антону Жулину.\n</para>\n<para lang=\"en\">\nresolver now randomly rotates addresses\nreturned from cache.<br/>\nThanks to Anton Jouline.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с OpenSSL 0.9.7.\n</para>\n<para lang=\"en\">\nOpenSSL 0.9.7 compatibility.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.6\" date=\"2012-09-12\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_gunzip_filter_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_gunzip_filter_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива memcached_gzip_flag.\n</para>\n<para lang=\"en\">\nthe \"memcached_gzip_flag\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр always директивы gzip_static.\n</para>\n<para lang=\"en\">\nthe \"always\" parameter of the \"gzip_static\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве \"limit_req\";\nошибка появилась в 1.1.14.<br/>\nСпасибо Charles Chen.\n</para>\n<para lang=\"en\">\nin the \"limit_req\" directive;\nthe bug had appeared in 1.1.14.<br/>\nThanks to Charles Chen.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался gcc 4.7 с оптимизацией -O2\nесли использовался параметр --with-ipv6.\n</para>\n<para lang=\"en\">\nnginx could not be built by gcc 4.7 with -O2 optimization\nif the --with-ipv6 option was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.5\" date=\"2012-08-21\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nмодуль ngx_http_mp4_module больше не отфильтровывает дорожки\nв форматах, отличных от H.264 и AAC.\n</para>\n<para lang=\"en\">\nthe ngx_http_mp4_module module no longer skips\ntracks in formats other than H.264 and AAC.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли в директиве map в качестве значений использовались переменные.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"map\" directive was used with variables as values.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault\nпри использовании директивы geo с параметром ranges,\nно без параметра default; ошибка появилась в 0.8.43.<br/>\nСпасибо Zhen Chen и Weibin Yao.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"geo\" directive was used with the \"ranges\" parameter\nbut without the \"default\" parameter; the bug had appeared in 0.8.43.<br/>\nThanks to Zhen Chen and Weibin Yao.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке параметра командной строки -p.\n</para>\n<para lang=\"en\">\nin the -p command-line parameter handling.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв почтовом прокси-сервере.\n</para>\n<para lang=\"en\">\nin the mail proxy server.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнезначительных потенциальных ошибок.<br/>\nСпасибо Coverity.\n</para>\n<para lang=\"en\">\nof minor potential bugs.<br/>\nThanks to Coverity.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не собирался с Visual Studio 2005 Express.<br/>\nСпасибо HAYASHI Kentaro.\n</para>\n<para lang=\"en\">\nnginx/Windows could not be built with Visual Studio 2005 Express.<br/>\nThanks to HAYASHI Kentaro.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.4\" date=\"2012-07-31\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь на слушающих IPv6-сокетах параметр ipv6only\nвключён по умолчанию.\n</para>\n<para lang=\"en\">\nthe \"ipv6only\" parameter is now turned on by default for\nlistening IPv6 sockets.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка компилятора Clang.\n</para>\n<para lang=\"en\">\nthe Clang compiler support.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмогли создаваться лишние слушающие сокеты.<br/>\nСпасибо Роману Одайскому.\n</para>\n<para lang=\"en\">\nextra listening sockets might be created.<br/>\nThanks to Roman Odaisky.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows мог нагружать процессор, если при запуске рабочего процесса\nпроисходила ошибка.<br/>\nСпасибо Ricardo Villalobos Guevara.\n</para>\n<para lang=\"en\">\nnginx/Windows might hog CPU if a worker process failed to start.<br/>\nThanks to Ricardo Villalobos Guevara.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_pass_header, fastcgi_pass_header, scgi_pass_header,\nuwsgi_pass_header, proxy_hide_header, fastcgi_hide_header,\nscgi_hide_header и uwsgi_hide_header\nмогли наследоваться некорректно.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass_header\", \"fastcgi_pass_header\", \"scgi_pass_header\",\n\"uwsgi_pass_header\", \"proxy_hide_header\", \"fastcgi_hide_header\",\n\"scgi_hide_header\", and \"uwsgi_hide_header\" directives\nmight be inherited incorrectly.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.3\" date=\"2012-07-10\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка entity tags и директива etag.\n</para>\n<para lang=\"en\">\nentity tags support and the \"etag\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы map с параметром hostnames\nне игнорировалась конечная точка в исходном значении.\n</para>\n<para lang=\"en\">\ntrailing dot in a source value was not ignored\nif the \"map\" directive was used with the \"hostnames\" parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдля обработки запроса мог использоваться неверный location,\nесли переход в именованный location происходил\nпосле изменения URI с помощью директивы rewrite.\n</para>\n<para lang=\"en\">\nincorrect location might be used to process a request\nif a URI was changed via a \"rewrite\" directive\nbefore an internal redirect to a named location.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.2\" date=\"2012-06-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр single директивы keepalive теперь игнорируется.\n</para>\n<para lang=\"en\">\nthe \"single\" parameter of the \"keepalive\" directive is now ignored.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nсжатие SSL теперь отключено\nв том числе при использовании OpenSSL старее 1.0.0.\n</para>\n<para lang=\"en\">\nSSL compression is now disabled when using all versions of OpenSSL,\nincluding ones prior to 1.0.0.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу \"ip_hash\" теперь можно использовать для балансировки IPv6 клиентов.\n</para>\n<para lang=\"en\">\nit is now possible to use the \"ip_hash\" directive to balance IPv6 clients.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременную $status теперь можно использовать не только в директиве log_format.\n</para>\n<para lang=\"en\">\nthe $status variable can now be used not only in the \"log_format\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри завершении рабочего процесса мог произойти segmentation fault,\nесли использовалась директива resolver.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process on shutdown\nif the \"resolver\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался модуль ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the ngx_http_mp4_module was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовались конфликтующие имена серверов с масками.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif conflicting wildcard server names were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна платформе ARM nginx мог аварийно завершаться по сигналу SIGBUS.\n</para>\n<para lang=\"en\">\nnginx might be terminated abnormally on a SIGBUS signal on ARM platform.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво время переконфигурации на HP-UX в лог\nзаписывался alert \"sendmsg() failed (9: Bad file number)\".\n</para>\n<para lang=\"en\">\nan alert \"sendmsg() failed (9: Bad file number)\" on HP-UX\nwhile reconfiguration.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.1\" date=\"2012-06-05\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nтеперь nginx/Windows игнорирует точку в конце компонента URI\nи не разрешает URI, содержащие последовательность \":$\".<br/>\nСпасибо Владимиру Кочеткову, Positive Research Center.\n</para>\n<para lang=\"en\">\nnow nginx/Windows ignores trailing dot in URI path component, and\ndoes not allow URIs with \":$\" in it.<br/>\nThanks to Vladimir Kochetkov, Positive Research Center.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_pass, fastcgi_pass, scgi_pass, uwsgi_pass и\nдиректива server в блоке upstream\nтеперь поддерживают IPv6-адреса.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\", \"fastcgi_pass\", \"scgi_pass\", \"uwsgi_pass\" directives, and\nthe \"server\" directive inside the \"upstream\" block,\nnow support IPv6 addresses.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв директиве resolver теперь можно указывать порт и\nзадавать IPv6-адреса DNS-серверов.\n</para>\n<para lang=\"en\">\nthe \"resolver\" directive now supports IPv6 addresses and\nan optional port specification.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива least_conn в блоке upstream.\n</para>\n<para lang=\"en\">\nthe \"least_conn\" directive inside the \"upstream\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпри использовании директивы ip_hash\nтеперь можно задавать веса серверов.\n</para>\n<para lang=\"en\">\nit is now possible to specify a weight for servers\nwhile using the \"ip_hash\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива image_filter;\nошибка появилась в 1.3.0.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"image_filter\" directive was used;\nthe bug had appeared in 1.3.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с модулем ngx_cpp_test_module;\nошибка появилась в 1.1.12.\n</para>\n<para lang=\"en\">\nnginx could not be built with ngx_cpp_test_module;\nthe bug had appeared in 1.1.12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдоступ к переменным из SSI и встроенного перла мог не работать после\nпереконфигурации.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\naccess to variables from SSI and embedded perl module might not work after\nreconfiguration.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_xslt_filter_module.<br/>\nСпасибо Kuramoto Eiji.\n</para>\n<para lang=\"en\">\nin the ngx_http_xslt_filter_module.<br/>\nThanks to Kuramoto Eiji.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти при использовании переменной $geoip_org.<br/>\nСпасибо Денису Латыпову.\n</para>\n<para lang=\"en\">\nmemory leak if $geoip_org variable was used.<br/>\nThanks to Denis F. Latypoff.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директивах proxy_cookie_domain и proxy_cookie_path.\n</para>\n<para lang=\"en\">\nin the \"proxy_cookie_domain\" and \"proxy_cookie_path\" directives.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.3.0\" date=\"2012-05-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива debug_connection теперь поддерживает IPv6-адреса\nи параметр \"unix:\".\n</para>\n<para lang=\"en\">\nthe \"debug_connection\" directive now supports IPv6 addresses\nand the \"unix:\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива set_real_ip_from и параметр proxy\nдирективы geo теперь поддерживают IPv6-адреса.\n</para>\n<para lang=\"en\">\nthe \"set_real_ip_from\" directive and the \"proxy\" parameter\nof the \"geo\" directive now support IPv6 addresses.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы real_ip_recursive, geoip_proxy и geoip_proxy_recursive.\n</para>\n<para lang=\"en\">\nthe \"real_ip_recursive\", \"geoip_proxy\", and \"geoip_proxy_recursive\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр proxy_recursive директивы geo.\n</para>\n<para lang=\"en\">\nthe \"proxy_recursive\" parameter of the \"geo\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива resolver.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"resolver\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовались директивы fastcgi_pass, scgi_pass или uwsgi_pass\nи бэкенд возвращал некорректный ответ.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"fastcgi_pass\", \"scgi_pass\", or \"uwsgi_pass\" directives were used\nand backend returned incorrect response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива rewrite и в новых аргументах запроса в строке\nзамены использовались переменные.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"rewrite\" directive was used and new request arguments\nin a replacement used variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог нагружать процессор,\nесли было достигнуто ограничение на количество открытых файлов.\n</para>\n<para lang=\"en\">\nnginx might hog CPU\nif the open file resource limit was reached.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы proxy_next_upstream с параметром http_404\nnginx мог бесконечно перебирать бэкенды, если в блоке upstream был\nхотя бы один сервер с флагом backup.\n</para>\n<para lang=\"en\">\nnginx might loop infinitely over backends\nif the \"proxy_next_upstream\" directive with the \"http_404\" parameter was used\nand there were backup servers specified in an upstream block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы ip_hash\nустановка параметра down директивы server\nмогла приводить к ненужному перераспределению клиентов между бэкендами.\n</para>\n<para lang=\"en\">\nadding the \"down\" parameter of the \"server\" directive\nmight cause unneeded client redistribution among backend servers\nif the \"ip_hash\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nsocket leak.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_fastcgi_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_fastcgi_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.2.0\" date=\"2012-04-23\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива try_files;\nошибка появилась в 1.1.19.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"try_files\" directive was used;\nthe bug had appeared in 1.1.19.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответ мог быть передан не полностью,\nесли использовалось больше IOV_MAX буферов.\n</para>\n<para lang=\"en\">\nresponse might be truncated\nif there were more than IOV_MAX buffers used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв работе параметра crop директивы image_filter.<br/>\nСпасибо Maxim Bublis.\n</para>\n<para lang=\"en\">\nin the \"crop\" parameter of the \"image_filter\" directive.<br/>\nThanks to Maxim Bublis.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.19\" date=\"2012-04-12\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри обработке специально созданного mp4 файла модулем ngx_http_mp4_module\nмогли перезаписываться области памяти рабочего процесса, что могло\nприводить к выполнению произвольного кода (CVE-2012-2089).<br/>\nСпасибо Matthew Daley.\n</para>\n<para lang=\"en\">\nspecially crafted mp4 file might allow to overwrite\nmemory locations in a worker process\nif the ngx_http_mp4_module was used,\npotentially resulting in arbitrary code execution (CVE-2012-2089).<br/>\nThanks to Matthew Daley.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows мог завершаться аварийно.<br/>\nСпасибо Vincent Lee.\n</para>\n<para lang=\"en\">\nnginx/Windows might be terminated abnormally.<br/>\nThanks to Vincent Lee.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx нагружал процессор, если все серверы в upstream'е были помечены\nфлагом backup.\n</para>\n<para lang=\"en\">\nnginx hogged CPU if all servers in an upstream were marked as \"backup\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы allow и deny могли наследоваться некорректно,\nесли в них использовались IPv6 адреса.\n</para>\n<para lang=\"en\">\nthe \"allow\" and \"deny\" directives might be inherited incorrectly\nif they were used with IPv6 addresses.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы modern_browser и ancient_browser\nмогли наследоваться некорректно.\n</para>\n<para lang=\"en\">\nthe \"modern_browser\" and \"ancient_browser\" directives\nmight be inherited incorrectly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтаймауты могли работать некорректно на Solaris/SPARC.\n</para>\n<para lang=\"en\">\ntimeouts might be handled incorrectly on Solaris/SPARC.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.18\" date=\"2012-03-28\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь keepalive соединения не запрещены для Safari по умолчанию.\n</para>\n<para lang=\"en\">\nkeepalive connections are no longer disabled for Safari by default.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $connection_requests.\n</para>\n<para lang=\"en\">\nthe $connection_requests variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd и\n$tcpinfo_rcv_space.\n</para>\n<para lang=\"en\">\n$tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd and\n$tcpinfo_rcv_space variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_cpu_affinity теперь работает на FreeBSD.\n</para>\n<para lang=\"en\">\nthe \"worker_cpu_affinity\" directive now works on FreeBSD.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы xslt_param и xslt_string_param.<br/>\nСпасибо Samuel Behan.\n</para>\n<para lang=\"en\">\nthe \"xslt_param\" and \"xslt_string_param\" directives.<br/>\nThanks to Samuel Behan.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв configure.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin configure tests.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_xslt_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_xslt_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Debian GNU/Hurd.\n</para>\n<para lang=\"en\">\nnginx could not be built on Debian GNU/Hurd.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.17\" date=\"2012-03-15\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nсодержимое ранее освобождённой памяти могло быть отправлено клиенту,\nесли бэкенд возвращал специально созданный ответ.<br/>\nСпасибо Matthew Daley.\n</para>\n<para lang=\"en\">\ncontent of previously freed memory might be sent to a client\nif backend returned specially crafted response.<br/>\nThanks to Matthew Daley.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании встроенного перла из SSI.<br/>\nСпасибо Matthew Daley.\n</para>\n<para lang=\"en\">\nin the embedded perl module if used from SSI.<br/>\nThanks to Matthew Daley.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_uwsgi_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_uwsgi_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.16\" date=\"2012-02-29\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nограничение на количество одновременных подзапросов поднято до 200.\n</para>\n<para lang=\"en\">\nthe simultaneous subrequest limit has been raised to 200.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр from в директиве disable_symlinks.\n</para>\n<para lang=\"en\">\nthe \"from\" parameter of the \"disable_symlinks\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы return и error_page теперь могут использоваться для возврата\nперенаправлений с кодом 307.\n</para>\n<para lang=\"en\">\nthe \"return\" and \"error_page\" directives can now be used to return 307\nredirections.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива resolver\nи на глобальном уровне не была задана директива error_log.<br/>\nСпасибо Роману Арутюняну.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"resolver\" directive was used\nand there was no \"error_log\" directive specified at global level.<br/>\nThanks to Roman Arutyunyan.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовались директивы \"proxy_http_version 1.1\" или\n\"fastcgi_keep_conn on\".\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif the \"proxy_http_version 1.1\" or \"fastcgi_keep_conn on\" directives\nwere used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечек памяти.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\nmemory leaks.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве disable_symlinks.\n</para>\n<para lang=\"en\">\nin the \"disable_symlinks\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании ZFS размер кэша на диске мог считаться некорректно;\nошибка появилась в 1.0.1.\n</para>\n<para lang=\"en\">\non ZFS filesystem disk cache size might be calculated incorrectly;\nthe bug had appeared in 1.0.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался компилятором icc 12.1.\n</para>\n<para lang=\"en\">\nnginx could not be built by the icc 12.1 compiler.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался gcc на Solaris;\nошибка появилась в 1.1.15.\n</para>\n<para lang=\"en\">\nnginx could not be built by gcc on Solaris;\nthe bug had appeared in 1.1.15.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.15\" date=\"2012-02-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива disable_symlinks.\n</para>\n<para lang=\"en\">\nthe \"disable_symlinks\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_cookie_domain и proxy_cookie_path.\n</para>\n<para lang=\"en\">\nthe \"proxy_cookie_domain\" and \"proxy_cookie_path\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог некорректно сообщать об ошибке \"upstream prematurely closed\nconnection\" вместо \"upstream sent too big header\".<br/>\nСпасибо Feibo Li.\n</para>\n<para lang=\"en\">\nnginx might log incorrect error \"upstream prematurely closed connection\"\ninstead of correct \"upstream sent too big header\" one.<br/>\nThanks to Feibo Li.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с модулем ngx_http_perl_module,\nесли использовался параметр --with-openssl.\n</para>\n<para lang=\"en\">\nnginx could not be built with the ngx_http_perl_module\nif the --with-openssl option was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nколичество внутренних перенаправлений в именованные location'ы\nне ограничивалось.\n</para>\n<para lang=\"en\">\nthe number of internal redirects to named locations was not limited.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nвызов $r->flush() несколько раз подряд мог приводить к ошибкам\nв модуле ngx_http_gzip_filter_module.\n</para>\n<para lang=\"en\">\ncalling $r->flush() multiple times might cause errors\nin the ngx_http_gzip_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы proxy_store с SSI-подзапросами\nвременные файлы могли не удаляться.\n</para>\n<para lang=\"en\">\ntemporary files might be not removed\nif the \"proxy_store\" directive was used with SSI includes.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв некоторых случаях некэшируемые переменные (такие, как $args)\nвозвращали старое пустое закэшированное значение.\n</para>\n<para lang=\"en\">\nin some cases non-cacheable variables (such as the $args variable)\nreturned old empty cached value.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли одновременно создавалось слишком много SSI-подзапросов;\nошибка появилась в 0.7.25.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif too many SSI subrequests were issued simultaneously;\nthe bug had appeared in 0.7.25.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.14\" date=\"2012-01-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь можно указать несколько ограничений limit_req одновременно.\n</para>\n<para lang=\"en\">\nmultiple \"limit_req\" limits may be used simultaneously.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок при соединении с бэкендом.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin error handling while connecting to a backend.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок при использовании AIO на FreeBSD.\n</para>\n<para lang=\"en\">\nin AIO error handling on FreeBSD.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв инициализации библиотеки OpenSSL.\n</para>\n<para lang=\"en\">\nin the OpenSSL library initialization.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_redirect могли наследоваться некорректно.\n</para>\n<para lang=\"en\">\nthe \"proxy_redirect\" directives might be inherited incorrectly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти при переконфигурации, если использовалась директива pcre_jit.\n</para>\n<para lang=\"en\">\nmemory leak during reconfiguration if the \"pcre_jit\" directive was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.13\" date=\"2012-01-16\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры TLSv1.1 и TLSv1.2 в директиве ssl_protocols.\n</para>\n<para lang=\"en\">\nthe \"TLSv1.1\" and \"TLSv1.2\" parameters of the \"ssl_protocols\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметры директивы limit_req наследовались некорректно;\nошибка появилась в 1.1.12.\n</para>\n<para lang=\"en\">\nthe \"limit_req\" directive parameters were not inherited correctly;\nthe bug had appeared in 1.1.12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_redirect некорректно обрабатывала заголовок Refresh\nпри использовании регулярных выражений.\n</para>\n<para lang=\"en\">\nthe \"proxy_redirect\" directive incorrectly processed \"Refresh\" header\nif regular expression were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_cache_use_stale с параметром error не возвращала ответ из\nкэша, если все бэкенды были признаны неработающими.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_use_stale\" directive with \"error\" parameter did not return\nanswer from cache if there were no live upstreams.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива worker_cpu_affinity могла не работать.\n</para>\n<para lang=\"en\">\nthe \"worker_cpu_affinity\" directive might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Solaris;\nошибка появилась в 1.1.12.\n</para>\n<para lang=\"en\">\nnginx could not be built on Solaris;\nthe bug had appeared in 1.1.12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.12\" date=\"2011-12-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпосле перенаправления запроса с помощью директивы error_page\nдиректива proxy_pass без URI теперь использует изменённый URI.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\na \"proxy_pass\" directive without URI part now uses changed URI\nafter redirection with the \"error_page\" directive.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy/fastcgi/scgi/uwsgi_cache_lock,\nproxy/fastcgi/scgi/uwsgi_cache_lock_timeout.\n</para>\n<para lang=\"en\">\nthe \"proxy/fastcgi/scgi/uwsgi_cache_lock\",\n\"proxy/fastcgi/scgi/uwsgi_cache_lock_timeout\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива pcre_jit.\n</para>\n<para lang=\"en\">\nthe \"pcre_jit\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nSSI команда if поддерживает выделения в регулярных выражениях.\n</para>\n<para lang=\"en\">\nthe \"if\" SSI command supports captures in regular expressions.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSI команда if не работала внутри команды block.\n</para>\n<para lang=\"en\">\nthe \"if\" SSI command did not work inside the \"block\" command.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы limit_conn_log_level и limit_req_log_level могли не работать.\n</para>\n<para lang=\"en\">\nthe \"limit_conn_log_level\" and \"limit_req_log_level\" directives might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива limit_rate не позволяла передавать на полной скорости,\nдаже если был указан очень большой лимит.\n</para>\n<para lang=\"en\">\nthe \"limit_rate\" directive did not allow to use full throughput,\neven if limit value was very high.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива sendfile_max_chunk не работала,\nесли использовалась директива limit_rate.\n</para>\n<para lang=\"en\">\nthe \"sendfile_max_chunk\" directive did not work,\nif the \"limit_rate\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве proxy_pass использовались переменные и не был указан URI,\nвсегда использовался URI исходного запроса.\n</para>\n<para lang=\"en\">\na \"proxy_pass\" directive without URI part always used original request URI\nif variables were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле перенаправления запроса с помощью директивы try_files\nдиректива proxy_pass без URI могла использовать URI исходного запроса.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\na \"proxy_pass\" directive without URI part might use original request\nafter redirection with the \"try_files\" directive.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_scgi_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_scgi_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Solaris;\nошибка появилась в 1.1.9.\n</para>\n<para lang=\"en\">\nnginx could not be built on Solaris;\nthe bug had appeared in 1.1.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.11\" date=\"2011-12-12\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр so_keepalive в директиве listen.<br/>\nСпасибо Всеволоду Стахову.\n</para>\n<para lang=\"en\">\nthe \"so_keepalive\" parameter of the \"listen\" directive.<br/>\nThanks to Vsevolod Stakhov.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр if_not_empty в директивах fastcgi/scgi/uwsgi_param.\n</para>\n<para lang=\"en\">\nthe \"if_not_empty\" parameter of the \"fastcgi/scgi/uwsgi_param\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $https.\n</para>\n<para lang=\"en\">\nthe $https variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_redirect поддерживает переменные в первом параметре.\n</para>\n<para lang=\"en\">\nthe \"proxy_redirect\" directive supports variables in the first parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_redirect поддерживает регулярные выражения.\n</para>\n<para lang=\"en\">\nthe \"proxy_redirect\" directive supports regular expressions.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $sent_http_cache_control могла содержать неверное значение при\nиспользовании директивы expires.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nthe $sent_http_cache_control variable might contain a wrong value if the\n\"expires\" directive was used.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива read_ahead могла не работать при использовании совместно с\ntry_files и open_file_cache.\n</para>\n<para lang=\"en\">\nthe \"read_ahead\" directive might not work combined with \"try_files\"\nand \"open_file_cache\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в параметре inactive директивы proxy_cache_path\nбыло указано малое время,\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif small time was used in the \"inactive\" parameter of\nthe \"proxy_cache_path\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы из кэша могли зависать.\n</para>\n<para lang=\"en\">\nresponses from cache might hang.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.10\" date=\"2011-11-30\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании AIO на Linux в рабочем процессе происходил segmentation fault;\nошибка появилась в 1.1.9.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in a worker process if AIO was used on Linux;\nthe bug had appeared in 1.1.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.9\" date=\"2011-11-28\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь двойные кавычки экранируется при выводе SSI-командой echo.<br/>\nСпасибо Зауру Абасмирзоеву.\n</para>\n<para lang=\"en\">\nnow double quotes are encoded in an \"echo\" SSI-command output.<br/>\nThanks to Zaur Abasmirzoev.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр valid в директиве resolver.  По умолчанию теперь\nиспользуется TTL, возвращённый DNS-сервером.<br/>\nСпасибо Кириллу Коринскому.\n</para>\n<para lang=\"en\">\nthe \"valid\" parameter of the \"resolver\" directive.  By default TTL\nreturned by a DNS server is used.<br/>\nThanks to Kirill A. Korinskiy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог перестать отвечать, если рабочий процесс завершался аварийно.\n</para>\n<para lang=\"en\">\nnginx might hang after a worker process abnormal termination.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалось SNI;\nошибка появилась в 1.1.2.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif SNI was used;\nthe bug had appeared in 1.1.2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве keepalive_disable;\nошибка появилась в 1.1.8.<br/>\nСпасибо Александру Усову.\n</para>\n<para lang=\"en\">\nin the \"keepalive_disable\" directive;\nthe bug had appeared in 1.1.8.<br/>\nThanks to Alexander Usov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсигнал SIGWINCH переставал работать после первого обновления исполняемого\nфайла;\nошибка появилась в 1.1.1.\n</para>\n<para lang=\"en\">\nSIGWINCH signal did not work after first binary upgrade;\nthe bug had appeared in 1.1.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь ответы бэкендов, длина которых не соответствует заголовку\nContent-Length, не кэширутся.\n</para>\n<para lang=\"en\">\nbackend responses with length not matching \"Content-Length\" header line\nare no longer cached.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве scgi_param при использовании составных параметров.\n</para>\n<para lang=\"en\">\nin the \"scgi_param\" directive, if complex parameters were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв методе epoll.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nin the \"epoll\" event method.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_flv_module.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nin the ngx_http_flv_module.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_mp4_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx понимает IPv6-адреса в строке запроса и в заголовке Host.\n</para>\n<para lang=\"en\">\nIPv6 addresses are now handled properly in a request line and in a \"Host\"\nrequest header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы add_header и expires не работали для ответов с кодом 206,\nесли запрос проксировался.\n</para>\n<para lang=\"en\">\n\"add_header\" and \"expires\" directives did not work if a request was proxied\nand response status code was 206.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на FreeBSD 10.\n</para>\n<para lang=\"en\">\nnginx could not be built on FreeBSD 10.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на AIX.\n</para>\n<para lang=\"en\">\nnginx could not be built on AIX.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.8\" date=\"2011-11-14\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nмодуль ngx_http_limit_zone_module переименован в ngx_http_limit_conn_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_limit_zone_module was renamed to the ngx_http_limit_conn_module.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива limit_zone заменена директивой limit_conn_zone с новым синтаксисом.\n</para>\n<para lang=\"en\">\nthe \"limit_zone\" directive was superseded by the \"limit_conn_zone\" directive\nwith a new syntax.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка ограничения по нескольким limit_conn на одном уровне.\n</para>\n<para lang=\"en\">\nsupport for multiple \"limit_conn\" limits on the same level.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива image_filter_sharpen.\n</para>\n<para lang=\"en\">\nthe \"image_filter_sharpen\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли resolver получил большой DNS-ответ.<br/>\nСпасибо Ben Hawkes.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif resolver got a big DNS response.<br/>\nThanks to Ben Hawkes.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв вычислении ключа для кэширования,\nесли использовалась внутренняя реализация MD5;\nошибка появилась в 1.0.4.\n</para>\n<para lang=\"en\">\nin cache key calculation\nif internal MD5 implementation was used;\nthe bug had appeared in 1.0.4.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстроки \"If-Modified-Since\", \"If-Range\" и им подобные в заголовке запроса\nклиента могли передаваться бэкенду при кэшировании; или не передаваться при\nвыключенном кэшировании, если кэширование было включено в другой части\nконфигурации.\n</para>\n<para lang=\"en\">\nthe \"If-Modified-Since\", \"If-Range\", etc. client request header lines\nmight be passed to backend while caching; or not passed without caching\nif caching was enabled in another part of the configuration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_mp4_module выдавал неверную строку \"Content-Length\"\nв заголовке ответа, использовался аргумент start.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nthe module ngx_http_mp4_module sent incorrect \"Content-Length\" response\nheader line if the \"start\" argument was used.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.7\" date=\"2011-10-31\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка нескольких DNS серверов в директиве \"resolver\".<br/>\nСпасибо Кириллу Коринскому.\n</para>\n<para lang=\"en\">\nsupport of several DNS servers in the \"resolver\" directive.<br/>\nThanks to Kirill A. Korinskiy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна старте или во время переконфигурации происходил segmentation fault,\nесли директива ssl использовалась на уровне http и не был указан\nssl_certificate.\n</para>\n<para lang=\"en\">\na segmentation fault occurred on start or during reconfiguration\nif the \"ssl\" directive was used at http level and there was\nno \"ssl_certificate\" defined.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nуменьшено потребление памяти при проксировании больших файлов,\nесли они буферизировались на диск.\n</para>\n<para lang=\"en\">\nreduced memory consumption while proxying big files\nif they were buffered to disk.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовалась директива \"proxy_http_version 1.1\".\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif \"proxy_http_version 1.1\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве \"expires @time\".\n</para>\n<para lang=\"en\">\nin the \"expires @time\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.6\" date=\"2011-10-17\">\n\n<change>\n<para lang=\"ru\">\nИзменение во внутреннем API: теперь при внутреннем редиректе\nв именованный location контексты модулей очищаются.<br/>\nПо запросу Yichun Zhang.\n</para>\n<para lang=\"en\">\nChange in internal API: now module context data are cleared\nwhile internal redirect to named location.<br/>\nRequested by Yichun Zhang.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь если сервер, описанный в блоке upstream, был признан неработающим,\nто после истечения fail_timeout на него будет отправлен только один запрос;\nсервер будет считаться работающим, если успешно ответит на этот запрос.\n</para>\n<para lang=\"en\">\nif a server in an upstream failed, only one request will be sent to it\nafter fail_timeout; the server will be considered alive if it will\nsuccessfully respond to the request.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь символы 0x7F-0xFF в access_log записываются в виде \\xXX.\n</para>\n<para lang=\"en\">\nnow the 0x7F-0xFF characters are escaped as \\xXX in an access_log.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы \"proxy/fastcgi/scgi/uwsgi_ignore_headers\" теперь поддерживают\nзначения X-Accel-Limit-Rate, X-Accel-Buffering и X-Accel-Charset.\n</para>\n<para lang=\"en\">\n\"proxy/fastcgi/scgi/uwsgi_ignore_headers\" directives support the following\nadditional values: X-Accel-Limit-Rate, X-Accel-Buffering, X-Accel-Charset.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nуменьшение потребления памяти при использовании SSL.\n</para>\n<para lang=\"en\">\ndecrease of memory consumption if SSL is used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнекоторые UTF-8 символы обрабатывались неправильно.<br/>\nСпасибо Алексею Куцу.\n</para>\n<para lang=\"en\">\nsome UTF-8 characters were processed incorrectly.<br/>\nThanks to Alexey Kuts.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы модуля ngx_http_rewrite_module, заданные на уровне server,\nприменялись повторно, если для запроса не находилось ни одного location'а.\n</para>\n<para lang=\"en\">\nthe ngx_http_rewrite_module directives specified at \"server\" level were\nexecuted twice if no matching locations were defined.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании \"aio sendfile\" могла происходить утечка сокетов.\n</para>\n<para lang=\"en\">\na socket leak might occurred if \"aio sendfile\" was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании файлового AIO соединения с быстрыми клиентами\nмогли быть закрыты по истечению send_timeout.\n</para>\n<para lang=\"en\">\nconnections with fast clients might be closed after send_timeout\nif file AIO was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_autoindex_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_autoindex_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_mp4_module не поддерживал перемотку на 32-битных платформах.\n</para>\n<para lang=\"en\">\nthe module ngx_http_mp4_module did not support seeking on 32-bit platforms.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.5\" date=\"2011-10-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы uwsgi_buffering и scgi_buffering.<br/>\nСпасибо Peter Smit.\n</para>\n<para lang=\"en\">\nthe \"uwsgi_buffering\" and \"scgi_buffering\" directives.<br/>\nThanks to Peter Smit.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании proxy_cache_bypass могли быть закэшированы\nнекэшируемые ответы.<br/>\nСпасибо John Ferlito.\n</para>\n<para lang=\"en\">\nnon-cacheable responses might be cached if \"proxy_cache_bypass\" directive\nwas used.<br/>\nThanks to John Ferlito.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_proxy_module при работе с бэкендами по HTTP/1.1.\n</para>\n<para lang=\"en\">\nin HTTP/1.1 support in the ngx_http_proxy_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзакэшированные ответы с пустым телом возвращались некорректно;\nошибка появилась в 0.8.31.\n</para>\n<para lang=\"en\">\ncached responses with an empty body were returned incorrectly;\nthe bug had appeared in 0.8.31.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы с кодом 201 модуля ngx_http_dav_module были некорректны;\nошибка появилась в 0.8.32.\n</para>\n<para lang=\"en\">\n201 responses of the ngx_http_dav_module were incorrect;\nthe bug had appeared in 0.8.32.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве return.\n</para>\n<para lang=\"en\">\nin the \"return\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы \"ssl_session_cache builtin\" происходил\nsegmentation fault;\nошибка появилась в 1.1.1.\n</para>\n<para lang=\"en\">\nthe \"ssl_session_cache builtin\" directive caused segmentation fault;\nthe bug had appeared in 1.1.1.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.4\" date=\"2011-09-20\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_upstream_keepalive.\n</para>\n<para lang=\"en\">\nthe ngx_http_upstream_keepalive module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_http_version.\n</para>\n<para lang=\"en\">\nthe \"proxy_http_version\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_keep_conn.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_keep_conn\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_aio_requests.\n</para>\n<para lang=\"en\">\nthe \"worker_aio_requests\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx был собран с файловым AIO,\nон не мог запускаться на Linux без поддержки AIO.\n</para>\n<para lang=\"en\">\nif nginx was built --with-file-aio it could not be run on Linux\nkernel which did not support AIO.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок при работе с Linux AIO.\n<br/>\nСпасибо Hagai Avrahami.\n</para>\n<para lang=\"en\">\nin Linux AIO error processing.\n<br/>\nThanks to Hagai Avrahami.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nуменьшено потребление памяти для долгоживущих запросов.\n</para>\n<para lang=\"en\">\nreduced memory consumption for long-lived requests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_mp4_module не поддерживал 64-битный MP4-атом co64.\n</para>\n<para lang=\"en\">\nthe module ngx_http_mp4_module did not support 64-bit MP4 \"co64\" atom.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.3\" date=\"2011-09-14\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_mp4_module.\n</para>\n<para lang=\"en\">\nthe module ngx_http_mp4_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв Linux AIO, используемым совместно с open_file_cache.\n</para>\n<para lang=\"en\">\nin Linux AIO combined with open_file_cache.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nopen_file_cache не обновлял информацию о файле,\nесли файл был изменён не атомарно.\n</para>\n<para lang=\"en\">\nopen_file_cache did not update file info on retest\nif file was not atomically changed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на MacOSX 10.7.\n</para>\n<para lang=\"en\">\nnginx could not be built on MacOSX 10.7.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.2\" date=\"2011-09-05\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь, если суммарный размер всех диапазонов больше размера исходного ответа,\nто nginx возвращает только исходный ответ, не обрабатывая диапазоны.\n</para>\n<para lang=\"en\">\nnow if total size of all ranges is greater than source response size,\nthen nginx disables ranges and returns just the source response.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива max_ranges.\n</para>\n<para lang=\"en\">\nthe \"max_ranges\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы ssl_verify_client, ssl_verify_depth и ssl_prefer_server_cipher\nмогли работать некорректно, если использовался SNI.\n</para>\n<para lang=\"en\">\nthe \"ssl_verify_client\", \"ssl_verify_depth\", and \"ssl_prefer_server_ciphers\"\ndirectives might work incorrectly if SNI was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директивах proxy/fastcgi/scgi/ uwsgi_ignore_client_abort.\n</para>\n<para lang=\"en\">\nin the \"proxy/fastcgi/scgi/uwsgi_ignore_client_abort\" directives.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.1\" date=\"2011-08-22\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь загрузчик кэша за каждую итерацию либо обрабатывает число файлов,\nуказанное в параметре load_files, либо работает не дольше времени,\nуказанного в параметре loader_threshold.\n</para>\n<para lang=\"en\">\nnow cache loader processes either as many files as specified by \"loader_files\"\nparameter or works no longer than time specified by the \"loader_threshold\"\nparameter during each iteration.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nSIGWINCH сигнал теперь работает только в режиме демона.\n</para>\n<para lang=\"en\">\nnow SIGWINCH signal works only in daemon mode.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь разделяемые зоны и кэши используют семафоры POSIX на Solaris.<br/>\nСпасибо Денису Иванову.\n</para>\n<para lang=\"en\">\nnow shared zones and caches use POSIX semaphores on Solaris.<br/>\nThanks to Den Ivanov.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь на NetBSD поддерживаются accept фильтры.\n</para>\n<para lang=\"en\">\naccept filters are now supported on NetBSD.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Linux 3.0.\n</para>\n<para lang=\"en\">\nnginx could not be built on Linux 3.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв некоторых случаях nginx не использовал сжатие;\nошибка появилась в 1.1.0.\n</para>\n<para lang=\"en\">\nnginx did not use gzipping in some cases;\nthe bug had appeared in 1.1.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nобработка тела запроса могла быть неверной, если клиент использовал pipelining.\n</para>\n<para lang=\"en\">\nrequest body might be processed incorrectly if client used pipelining.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве request_body_in_single_buf.\n</para>\n<para lang=\"en\">\nin the \"request_body_in_single_buf\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директивах proxy_set_body и proxy_pass_request_body\nпри использовании SSL-соединения с бэкендом.\n</para>\n<para lang=\"en\">\nin \"proxy_set_body\" and \"proxy_pass_request_body\" directives\nif SSL connection to backend was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx нагружал процессор, если все серверы в upstream'е были помечены\nфлагом down.\n</para>\n<para lang=\"en\">\nnginx hogged CPU if all servers in an upstream were marked as \"down\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри переконфигурации мог произойти segmentation fault,\nесли в предыдущей конфигурации был определён, но не использовался\nssl_session_cache.\n</para>\n<para lang=\"en\">\na segmentation fault might occur during reconfiguration\nif ssl_session_cache was defined but not used in previous configuration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании большого количества backup-серверов\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif many backup servers were used in an upstream.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директив fastcgi/scgi/uwsgi_param\nсо значениями, начинающимися со строки \"HTTP_\",\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.8.40.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nif \"fastcgi/scgi/uwsgi_param\" directives were used\nwith values starting with \"HTTP_\";\nthe bug had appeared in 0.8.40.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.1.0\" date=\"2011-08-01\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nуменьшение времени работы загрузчика кэша.\n</para>\n<para lang=\"en\">\ncache loader run time decrease.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры loader_files, loader_sleep и loader_threshold\nдиректив proxy/fastcgi/scgi/uwsgi_cache_path.\n</para>\n<para lang=\"en\">\n\"loader_files\", \"loader_sleep\", and \"loader_threshold\" options\nof the \"proxy/fastcgi/scgi/uwsgi_cache_path\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nуменьшение времени загрузки конфигураций с большим количеством HTTPS серверов.\n</para>\n<para lang=\"en\">\nloading time decrease of configuration with large number of HTTPS sites.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx поддерживает шифры с обменом ECDHE-ключами.<br/>\nСпасибо Adrian Kotelba.\n</para>\n<para lang=\"en\">\nnow nginx supports ECDHE key exchange ciphers.<br/>\nThanks to Adrian Kotelba.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива lingering_close.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe \"lingering_close\" directive.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзакрытия соединения для pipelined-запросов.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nin closing connection for pipelined requests.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не запрещал сжатие при получении значения \"gzip;q=0\"\nв строке \"Accept-Encoding\" в заголовке запроса клиента.\n</para>\n<para lang=\"en\">\nnginx did not disable gzipping if client sent \"gzip;q=0\" in\n\"Accept-Encoding\" request header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтаймаута при небуферизированном проксировании.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nin timeout in unbuffered proxied mode.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти при использовании переменных в директиве proxy_pass\nпри работе с бэкендом по HTTPS.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nmemory leaks when a \"proxy_pass\" directive contains variables and proxies\nto an HTTPS backend.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв проверке параметра директивы proxy_pass, заданного переменными.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\nin parameter validation of a \"proxy_pass\" directive with variables.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL не работал на QNX.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nSSL did not work on QNX.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL модули не собирались gcc 4.6 без параметра --with-debug.\n</para>\n<para lang=\"en\">\nSSL modules could not be built by gcc 4.6 without --with-debug option.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.0.5\" date=\"2011-07-19\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию используются следующие шифры SSL: \"HIGH:!aNULL:!MD5\".<br/>\nСпасибо Rob Stradling.\n</para>\n<para lang=\"en\">\nnow default SSL ciphers are \"HIGH:!aNULL:!MD5\".<br/>\nThanks to Rob Stradling.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы referer_hash_max_size и referer_hash_bucket_size.<br/>\nСпасибо Witold Filipczyk.\n</para>\n<para lang=\"en\">\nthe \"referer_hash_max_size\" and \"referer_hash_bucket_size\"\ndirectives.<br/>\nThanks to Witold Filipczyk.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $uid_reset.\n</para>\n<para lang=\"en\">\n$uid_reset variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании кэширования\nв рабочем процессе мог произойти segmentation fault.<br/>\nСпасибо Lanshun Zhou.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process,\nif a caching was used.<br/>\nThanks to Lanshun Zhou.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании кэширования рабочие процессы\nмогли зациклиться во время переконфигурации;\nошибка появилась в 0.8.48.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nworker processes may got caught in an endless loop during reconfiguration,\nif a caching was used;\nthe bug had appeared in 0.8.48.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсообщения \"stalled cache updating\".<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\n\"stalled cache updating\" alert.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.0.4\" date=\"2011-06-01\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь в регулярных выражениях в директиве map можно задать\nчувствительность к регистру с помощью префиксов \"~\" и \"~*\".\n</para>\n<para lang=\"en\">\nnow regular expressions case sensitivity in the \"map\" directive\nis given by prefixes \"~\" or \"~*\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь разделяемые зоны и кэши используют семафоры POSIX на Linux.<br/>\nСпасибо Денису Латыпову.\n</para>\n<para lang=\"en\">\nnow shared zones and caches use POSIX semaphores on Linux.<br/>\nThanks to Denis F. Latypoff.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсообщения \"stalled cache updating\".\n</para>\n<para lang=\"en\">\n\"stalled cache updating\" alert.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-http_auth_basic_module;\nошибка появилась в 1.0.3.\n</para>\n<para lang=\"en\">\nnginx could not be built --without-http_auth_basic_module;\nthe bug had appeared in 1.0.3.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.0.3\" date=\"2011-05-25\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива auth_basic_user_file поддерживает шифрование пароля\nметодами \"$apr1\", \"{PLAIN}\" и \"{SSHA}\".<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe \"auth_basic_user_file\" directive supports \"$apr1\", \"{PLAIN}\",\nand \"{SSHA}\" password encryption methods.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива geoip_org и переменная $geoip_org.<br/>\nСпасибо Александру Ускову, Arnaud Granal и Денису Латыпову.\n</para>\n<para lang=\"en\">\nthe \"geoip_org\" directive and $geoip_org variable.<br/>\nThanks to Alexander Uskov, Arnaud Granal, and Denis F. Latypoff.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодули ngx_http_geo_module и ngx_http_geoip_module поддерживают\nадреса IPv4, отображённые на IPv6 адреса.\n</para>\n<para lang=\"en\">\nngx_http_geo_module and ngx_http_geoip_module support IPv4 addresses\nmapped to IPv6 addresses.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проверке адреса IPv4, отображённого на адрес IPv6,\nв рабочем процессе происходил segmentation fault,\nесли директивы access или deny были определены только для адресов IPv6;\nошибка появилась в 0.8.22.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in a worker process\nduring testing IPv4 address mapped to IPv6 address,\nif access or deny rules were defined only for IPv6;\nthe bug had appeared in 0.8.22.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзакэшированный ответ мог быть испорчен, если значения директив\nproxy/fastcgi/scgi/uwsgi_cache_bypass и proxy/fastcgi/scgi/ uwsgi_no_cache\nбыли разными;\nошибка появилась в 0.8.46.\n</para>\n<para lang=\"en\">\na cached response may be broken if \"proxy/fastcgi/scgi/ uwsgi_cache_bypass\"\nand \"proxy/fastcgi/scgi/uwsgi_no_cache\" directive values were different;\nthe bug had appeared in 0.8.46.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.0.2\" date=\"2011-05-10\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь разделяемые зоны и кэши используют семафоры POSIX.\n</para>\n<para lang=\"en\">\nnow shared zones and caches use POSIX semaphores.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв работе параметра rotate директивы image_filter.<br/>\nСпасибо Adam Bocim.\n</para>\n<para lang=\"en\">\nin the \"rotate\" parameter of the \"image_filter\" directive.<br/>\nThanks to Adam Bocim.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Solaris;\nошибка появилась в 1.0.1.\n</para>\n<para lang=\"en\">\nnginx could not be built on Solaris;\nthe bug had appeared in 1.0.1.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.0.1\" date=\"2011-05-03\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директива split_clients использует алгоритм MurmurHash2 из-за\nлучшего распределения.<br/>\nСпасибо Олегу Мамонтову.\n</para>\n<para lang=\"en\">\nnow the \"split_clients\" directive uses MurmurHash2 algorithm because\nof better distribution.<br/>\nThanks to Oleg Mamontov.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь длинные строки, начинающиеся с нуля, не считаются ложными\nзначениями.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow long strings starting with zero are not considered as false values.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию nginx использует значение 511 для listen backlog на Linux.\n</para>\n<para lang=\"en\">\nnow nginx uses a default listen backlog value 511 on Linux.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $upstream_... можно использовать в SSI и перловом модулях.\n</para>\n<para lang=\"en\">\nthe $upstream_... variables may be used in the SSI and perl modules.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx лучше ограничивает размер кэша на диске.<br/>\nСпасибо Олегу Мамонтову.\n</para>\n<para lang=\"en\">\nnow nginx limits better disk cache size.<br/>\nThanks to Oleg Mamontov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри парсинге неправильного IPv4 адреса мог произойти segmentation fault;\nошибка появилась в 0.8.22.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na segmentation fault might occur while parsing incorrect IPv4 address;\nthe bug had appeared in 0.9.3.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался gcc 4.6 без параметра --with-debug.\n</para>\n<para lang=\"en\">\nnginx could not be built by gcc 4.6 without --with-debug option.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Solaris 9 и более ранних;\nошибка появилась в 0.9.3.<br/>\nСпасибо Dagobert Michelsen.\n</para>\n<para lang=\"en\">\nnginx could not be built on Solaris 9 and earlier;\nthe bug had appeared in 0.9.3.<br/>\nThanks to Dagobert Michelsen.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $request_time имела неверные значения, если использовались\nподзапросы;\nошибка появилась в 0.8.47.<br/>\nСпасибо Игорю А. Валькову.\n</para>\n<para lang=\"en\">\n$request_time variable had invalid values if subrequests were used;\nthe bug had appeared in 0.8.47.<br/>\nThanks to Igor A. Valcov.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"1.0.0\" date=\"2011-04-12\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\ncache manager мог нагружать процессор после переконфигурации.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na cache manager might hog CPU after reload.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"image_filter crop\" неправильно работала в сочетании с\n\"image_filter rotate 180\".\n</para>\n<para lang=\"en\">\nan \"image_filter crop\" directive worked incorrectly coupled with\nan \"image_filter rotate 180\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"satisfy any\" запрещала выдачу пользовательской страницы\nдля 401 кода.\n</para>\n<para lang=\"en\">\na \"satisfy any\" directive disabled custom 401 error page.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.7\" date=\"2011-04-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь соединения в состоянии keepalive могут быть закрыты преждевременно,\nесли у воркера нет свободных соединений.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow keepalive connections may be closed premature,\nif there are no free worker connections.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр rotate директивы image_filter.<br/>\nСпасибо Adam Bocim.\n</para>\n<para lang=\"en\">\nthe \"rotate\" parameter of the \"image_filter\" directive.<br/>\nThanks to Adam Bocim.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nситуации, когда бэкенд в директивах fastcgi_pass, scgi_pass или uwsgi_pass\nзадан выражением и ссылается на описанный upstream.\n</para>\n<para lang=\"en\">\na case when a backend in \"fastcgi_pass\", \"scgi_pass\", or \"uwsgi_pass\"\ndirectives is given by expression and refers to a defined upstream.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.6\" date=\"2011-03-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива map поддерживает регулярные выражения в качестве значения\nпервого параметра.\n</para>\n<para lang=\"en\">\nthe \"map\" directive supports regular expressions as value of the first\nparameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $time_iso8601 для access_log.<br/>\nСпасибо Michael Lustfield.\n</para>\n<para lang=\"en\">\n$time_iso8601 access_log variable.<br/>\nThanks to Michael Lustfield.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.5\" date=\"2011-02-21\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию nginx использует значение -1 для listen backlog\nна Linux.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nnow nginx uses a default listen backlog value -1 on Linux.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр utf8 в директивах geoip_country и geoip_city.<br/>\nСпасибо Денису Латыпову.\n</para>\n<para lang=\"en\">\nthe \"utf8\" parameter of \"geoip_country\" and \"geoip_city\" directives.<br/>\nThanks to Denis F. Latypoff.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nисправление в умолчательной директиве proxy_redirect, если в директиве\nproxy_pass не был описан URI.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nin a default \"proxy_redirect\" directive if \"proxy_pass\" directive has no\nURI part.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива error_page не работала с нестандартными кодами ошибок;\nошибка появилась в 0.8.53.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nan \"error_page\" directive did not work with nonstandard error codes;\nthe bug had appeared in 0.8.53.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.4\" date=\"2011-01-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_name поддерживает переменную $hostname.\n</para>\n<para lang=\"en\">\nthe \"server_name\" directive supports the $hostname variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\n494 код для ошибки \"Request Header Too Large\".\n</para>\n<para lang=\"en\">\n494 code for \"Request Header Too Large\" error.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.3\" date=\"2010-12-13\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли для пары IPv6-адрес:порт описан только один сервер, то выделения\nв регулярных выражениях в директиве server_name не работали.\n</para>\n<para lang=\"en\">\nif there was a single server for given IPv6 address:port pair,\nthen captures in regular expressions in a \"server_name\" directive did not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под Solaris;\nошибка появилась в 0.9.0.\n</para>\n<para lang=\"en\">\nnginx could not be built on Solaris;\nthe bug had appeared in 0.9.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.2\" date=\"2010-12-06\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка строки \"If-Unmodified-Since\" в заголовке запроса клиента.\n</para>\n<para lang=\"en\">\nthe \"If-Unmodified-Since\" client request header line support.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nиспользование accept(), если accept4() не реализован;\nошибка появилась в 0.9.0.\n</para>\n<para lang=\"en\">\nfallback to accept() syscall if accept4() was not implemented;\nthe issue had appeared in 0.9.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под Cygwin;\nошибка появилась в 0.9.0.\n</para>\n<para lang=\"en\">\nnginx could not be built on Cygwin;\nthe bug had appeared in 0.9.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nуязвимости в OpenSSL CVE-2010-4180.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nfor OpenSSL vulnerability CVE-2010-4180.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.1\" date=\"2010-11-30\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы вида \"return CODE message\" не работали;\nошибка появилась в 0.9.0.\n</para>\n<para lang=\"en\">\n\"return CODE message\" directives did not work;\nthe bug had appeared in 0.9.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.9.0\" date=\"2010-11-29\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива keepalive_disable.\n</para>\n<para lang=\"en\">\nthe \"keepalive_disable\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива map поддерживает переменные в качестве значения определяемой\nпеременной.\n</para>\n<para lang=\"en\">\nthe \"map\" directive supports variables as value of a defined variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива map поддерживает пустые строки в качестве значения первого параметра.\n</para>\n<para lang=\"en\">\nthe \"map\" directive supports empty strings as value of the first parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива map поддерживает выражения в первом параметре.\n</para>\n<para lang=\"en\">\nthe \"map\" directive supports expressions as the first parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nстраница руководства nginx(8).<br/>\nСпасибо Сергею Осокину.\n</para>\n<para lang=\"en\">\nnginx(8) manual page.<br/>\nThanks to Sergey Osokin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка accept4() в Linux.<br/>\nСпасибо Simon Liu.\n</para>\n<para lang=\"en\">\nLinux accept4() support.<br/>\nThanks to Simon Liu.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nустранение предупреждения линкера о \"sys_errlist\" и \"sys_nerr\" под Linux;\nпредупреждение появилось в 0.8.35.\n</para>\n<para lang=\"en\">\nelimination of Linux linker warning about \"sys_errlist\" and \"sys_nerr\";\nthe warning had appeared in 0.8.35.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы auth_basic\nв рабочем процессе мог произойти segmentation fault.<br/>\nСпасибо Михаилу Лалетину.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process,\nif the \"auth_basic\" directive was used.<br/>\nThanks to Michail Laletin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с модулем ngx_http_eval_module;\nошибка появилась в 0.8.42.\n</para>\n<para lang=\"en\">\ncompatibility with ngx_http_eval_module;\nthe bug had appeared in 0.8.42.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.53\" date=\"2010-10-18\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива error_page позволяет менять код статуса у редиректа.\n</para>\n<para lang=\"en\">\nnow the \"error_page\" directive allows to change a status code in a redirect.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива gzip_disable поддерживает специальную маску degradation.\n</para>\n<para lang=\"en\">\nthe \"gzip_disable\" directive supports special \"degradation\" mask.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании файлового AIO могла происходить утечка сокетов.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na socket leak might occurred if file AIO was used.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в первом сервере не была описана директива listen и нигде явно\nне описан сервер по умолчанию, то сервером по умолчанию становился\nследующий сервер с директивой listen;\nошибка появилась в 0.8.21.\n</para>\n<para lang=\"en\">\nif the first server had no \"listen\" directive and there was no explicit\ndefault server, then a next server with a \"listen\" directive became\nthe default server;\nthe bug had appeared in 0.8.21.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.52\" date=\"2010-09-28\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx использовал режим SSL для listen сокета, если для него был\nустановлен любой listen-параметр;\nошибка появилась в 0.8.51.\n</para>\n<para lang=\"en\">\nnginx used SSL mode for a listen socket if any listen option was set;\nthe bug had appeared in 0.8.51.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.51\" date=\"2010-09-27\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива secure_link_expires упразднена.\n</para>\n<para lang=\"en\">\nthe \"secure_link_expires\" directive has been canceled.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nуровень логгирования ошибок resolver'а понижен с уровня alert на error.\n</para>\n<para lang=\"en\">\na logging level of resolver errors has been lowered from \"alert\" to \"error\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь параметр \"ssl\" listen-сокета можно устанавливать несколько раз.\n</para>\n<para lang=\"en\">\nnow a listen socket \"ssl\" parameter may be set several times.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.50\" date=\"2010-09-02\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы secure_link, secure_link_md5 и secure_link_expires\nмодуля ngx_http_secure_link_module.\n</para>\n<para lang=\"en\">\nthe \"secure_link\", \"secure_link_md5\", and \"secure_link_expires\" directives of\nthe ngx_http_secure_link_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -q.<br/>\nСпасибо Геннадию Махомеду.\n</para>\n<para lang=\"en\">\nthe -q switch.<br/>\nThanks to Gena Makhomed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании кэширования рабочие процессы и могли зациклиться\nво время переконфигурации;\nошибка появилась в 0.8.48.\n</para>\n<para lang=\"en\">\nworker processes may got caught in an endless loop during reconfiguration,\nif a caching was used;\nthe bug had appeared in 0.8.48.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве gzip_disable.<br/>\nСпасибо Derrick Petzold.\n</para>\n<para lang=\"en\">\nin the \"gzip_disable\" directive.<br/>\nThanks to Derrick Petzold.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не мог посылать сигналы stop, quit, reopen, reload процессу,\nзапущенному в другой сессии.\n</para>\n<para lang=\"en\">\nnginx/Windows could not send stop, quit, reopen, and reload signals\nto a process run in other session.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.49\" date=\"2010-08-09\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива image_filter_jpeg_quality поддерживает переменные.\n</para>\n<para lang=\"en\">\nthe \"image_filter_jpeg_quality\" directive supports variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменной $geoip_region_name\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.8.48.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process,\nif the $geoip_region_name variables was used;\nthe bug had appeared in 0.8.48.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки, перехваченные error_page, кэшировались только до следующего запроса;\nошибка появилась в 0.8.48.\n</para>\n<para lang=\"en\">\nerrors intercepted by error_page were cached only for next request;\nthe bug had appeared in 0.8.48.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.48\" date=\"2010-08-03\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию директива server_name имеет значение пустое имя \"\".<br/>\nСпасибо Геннадию Махомеду.\n</para>\n<para lang=\"en\">\nnow the \"server_name\" directive default value is an empty name \"\".<br/>\nThanks to Gena Makhomed.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию директива server_name_in_redirect имеет значение off.\n</para>\n<para lang=\"en\">\nnow the \"server_name_in_redirect\" directive default value is \"off\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $geoip_dma_code, $geoip_area_code и $geoip_region_name.<br/>\nСпасибо Christine McGonagle.\n</para>\n<para lang=\"en\">\nthe $geoip_dma_code, $geoip_area_code, and $geoip_region_name variables.<br/>\nThanks to Christine McGonagle.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_pass, fastcgi_pass, uwsgi_pass и scgi_pass не наследовались\nв блоки limit_except.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\", \"fastcgi_pass\", \"uwsgi_pass\", and \"scgi_pass\" directives\nwere not inherited inside \"limit_except\" blocks.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_cache_min_uses, fastcgi_cache_min_uses\nuwsgi_cache_min_uses и scgi_cache_min_uses не работали;\nошибка появилась в 0.8.46.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_min_uses\", \"fastcgi_cache_min_uses\"\n\"uwsgi_cache_min_uses\", and \"scgi_cache_min_uses\" directives did not work;\nthe bug had appeared in 0.8.46.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива fastcgi_split_path_info неверно использовала выделения,\nесли в выделения попадала только часть URI.<br/>\nСпасибо Юрию Тарадаю и Frank Enderle.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_split_path_info\" directive used incorrectly captures,\nif only parts of an URI were captured.<br/>\nThanks to Yuriy Taraday and Frank Enderle.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива rewrite не экранировала символ \";\" при копировании из URI\nв аргументы.<br/>\nСпасибо Daisuke Murase.\n</para>\n<para lang=\"en\">\nthe \"rewrite\" directive did not escape a \";\" character during copying\nfrom URI to query string.<br/>\nThanks to Daisuke Murase.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_image_filter_module закрывал соединение,\nесли изображение было больше размера image_filter_buffer.\n</para>\n<para lang=\"en\">\nthe ngx_http_image_filter_module closed a connection,\nif an image was larger than \"image_filter_buffer\" size.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.47\" date=\"2010-07-28\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $request_time имела неверные значения для подзапросов.\n</para>\n<para lang=\"en\">\n$request_time variable had invalid values for subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки, перехваченные error_page, не кэшировались.\n</para>\n<para lang=\"en\">\nerrors intercepted by error_page could not be cached.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовался параметр max_size, то cache manager мог зациклиться;\nошибка появилась в 0.8.46.\n</para>\n<para lang=\"en\">\na cache manager process may got caught in an endless loop,\nif max_size parameter was used;\nthe bug had appeared in 0.8.46.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.46\" date=\"2010-07-19\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы proxy_no_cache, fastcgi_no_cache, uwsgi_no_cache\nи scgi_no_cache теперь влияют только на сохранение закэшированного ответа.\n</para>\n<para lang=\"en\">\nnow the \"proxy_no_cache\", \"fastcgi_no_cache\", \"uwsgi_no_cache\", and\n\"scgi_no_cache\" directives affect on a cached response saving only.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_cache_bypass, fastcgi_cache_bypass, uwsgi_cache_bypass\nи scgi_cache_bypass.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_bypass\", \"fastcgi_cache_bypass\", \"uwsgi_cache_bypass\",\nand \"scgi_cache_bypass\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не освобождал память в keys_zone кэшей в случае ошибки работы с\nбэкендом: память освобождалась только по истечении времени неактивности\nили при недостатке памяти.\n</para>\n<para lang=\"en\">\nnginx did not free memory in cache keys zones if there was an error\nduring working with backend: the memory was freed only after inactivity\ntime or on memory low condition.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.45\" date=\"2010-07-13\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nулучшения в модуле ngx_http_xslt_filter.<br/>\nСпасибо Laurence Rowe.\n</para>\n<para lang=\"en\">\nngx_http_xslt_filter improvements.<br/>\nThanks to Laurence Rowe.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответ SSI модуля мог передаваться не полностью после команды include\nс параметром wait=\"yes\";\nошибка появилась в 0.7.25.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nSSI response might be truncated after include with wait=\"yes\";\nthe bug had appeared in 0.7.25.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива listen не поддерживала параметр setfib=0.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive did not support the \"setfib=0\" parameter.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.44\" date=\"2010-07-05\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx по умолчанию не кэширует ответы бэкендов,\nв заголовке которых есть строка \"Set-Cookie\".\n</para>\n<para lang=\"en\">\nnow nginx does not cache by default backend responses,\nif they have a \"Set-Cookie\" header line.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива listen поддерживает параметр setfib.<br/>\nСпасибо Андрею Филонову.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive supports the \"setfib\" parameter.<br/>\nThanks to Andrew Filonov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива sub_filter могла изменять регистр букв при частичном совпадении.\n</para>\n<para lang=\"en\">\nthe \"sub_filter\" directive might change character case on partial match.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с HP/UX.\n</para>\n<para lang=\"en\">\ncompatibility with HP/UX.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с компилятором AIX xlC_r.\n</para>\n<para lang=\"en\">\ncompatibility with AIX xlC_r compiler.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx считал большие пакеты SSLv2 как обычные текстовые запросы.<br/>\nСпасибо Miroslaw Jaworski.\n</para>\n<para lang=\"en\">\nnginx treated large SSLv2 packets as plain requests.<br/>\nThanks to Miroslaw Jaworski.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.43\" date=\"2010-06-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nускорение загрузки больших баз geo-диапазонов.\n</para>\n<para lang=\"en\">\nlarge geo ranges base loading speed-up.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nперенаправление ошибки в \"location /zero {return 204;}\" без изменения\nкода ответа оставляло тело ошибки;\nошибка появилась в 0.8.42.\n</para>\n<para lang=\"en\">\nan error_page redirection to \"location /zero {return 204;}\" without\nchanging status code kept the error body;\nthe bug had appeared in 0.8.42.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог закрывать IPv6 listen сокет во время переконфигурации.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnginx might close IPv6 listen socket during reconfiguration.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременную $uid_set можно использовать на любой стадии обработки запроса.\n</para>\n<para lang=\"en\">\nthe $uid_set variable may be used at any request processing stage.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.42\" date=\"2010-06-21\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx проверяет location'ы, заданные регулярными выражениями,\nесли запрос полностью совпал с location'ом, заданным строкой префикса.\nПредыдущее поведение появилось в 0.7.1.\n</para>\n<para lang=\"en\">\nnow nginx tests locations given by regular expressions,\nif request was matched exactly by a location given by a prefix string.\nThe previous behavior has been introduced in 0.7.1.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_scgi_module.<br/>\nСпасибо Manlio Perillo.\n</para>\n<para lang=\"en\">\nthe ngx_http_scgi_module.<br/>\nThanks to Manlio Perillo.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв директиве return можно добавлять текст ответа.\n</para>\n<para lang=\"en\">\na text answer may be added to a \"return\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.41\" date=\"2010-06-15\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nрабочий процесс nginx/Windows мог завершаться аварийно при запросе файла\nс неверной кодировкой UTF-8.\n</para>\n<para lang=\"en\">\nnginx/Windows worker might be terminated abnormally if a requested file name\nhas invalid UTF-8 encoding.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx разрешает использовать пробелы в строке запроса.\n</para>\n<para lang=\"en\">\nnow nginx allows to use spaces in a request line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_redirect неправильно изменяла строку \"Refresh\" в заголовке\nответа бэкенда.<br/>\nСпасибо Андрею Андрееву и Максиму Согину.\n</para>\n<para lang=\"en\">\nthe \"proxy_redirect\" directive changed incorrectly a backend \"Refresh\"\nresponse header line.<br/>\nThanks to Andrey Andreew and Max Sogin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не поддерживал путь без имени хоста в\nстроке \"Destination\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnginx did not support path without host name\nin \"Destination\" request header line.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.40\" date=\"2010-06-07\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nтеперь nginx/Windows игнорирует имя потока файла по умолчанию.<br/>\nСпасибо Jose Antonio Vazquez Gonzalez.\n</para>\n<para lang=\"en\">\nnow nginx/Windows ignores default file stream name.<br/>\nThanks to Jose Antonio Vazquez Gonzalez.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_uwsgi_module.<br/>\nСпасибо Roberto De Ioris.\n</para>\n<para lang=\"en\">\nthe ngx_http_uwsgi_module.<br/>\nThanks to Roberto De Ioris.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_param со значением, начинающимся со строки \"HTTP_\",\nизменяет строку заголовка в запросе клиента.\n</para>\n<para lang=\"en\">\na \"fastcgi_param\" directive with value starting with \"HTTP_\" overrides\na client request header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстроки \"If-Modified-Since\", \"If-Range\" и им подобные в заголовке запроса\nклиента передавались FastCGI-серверу при кэшировании.\n</para>\n<para lang=\"en\">\nthe \"If-Modified-Since\", \"If-Range\", etc. client request header lines\nwere passed to FastCGI-server while caching.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nlisten unix domain сокет нельзя было изменить во время переконфигурации.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nlisten unix domain socket could not be changed during reconfiguration.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.39\" date=\"2010-05-31\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнаследуемая директива alias неправильно работала во вложенном location'е.\n</para>\n<para lang=\"en\">\nan inherited \"alias\" directive worked incorrectly in inclusive location.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв комбинации директив alias с переменными и try_files;\n</para>\n<para lang=\"en\">\nin \"alias\" with variables and \"try_files\" directives combination.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nlisten unix domain и IPv6 сокеты не наследовались во время обновления\nбез перерыва.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nlisten unix domain and IPv6 sockets did not inherit while online upgrade.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.38\" date=\"2010-05-24\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_no_cache и fastcgi_no_cache.\n</para>\n<para lang=\"en\">\nthe \"proxy_no_cache\" and \"fastcgi_no_cache\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при использовании переменной $scheme в директиве rewrite\nавтоматически делается редирект.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nnow the \"rewrite\" directive does a redirect automatically\nif the $scheme variable is used.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь задержки в директиве limit_req соответствует описанному алгоритму.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow \"limit_req\" delay directive conforms to the described algorithm.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременную $uid_got нельзя было использовать в SSI и перловом модулях.\n</para>\n<para lang=\"en\">\nthe $uid_got variable might not be used in the SSI and perl modules.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.37\" date=\"2010-05-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_split_clients_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_split_clients_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива map поддерживает ключи больше 255 символов.\n</para>\n<para lang=\"en\">\nthe \"map\" directive supports keys more than 255 characters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx игнорировал значения \"private\" и \"no-store\" в строке \"Cache-Control\"\nв заголовке ответа бэкенда.\n</para>\n<para lang=\"en\">\nnginx ignored the \"private\" and \"no-store\" values\nin the \"Cache-Control\" backend response header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр stub в SSI-директиве include не использовался,\nесли пустой ответ имел код 200.\n</para>\n<para lang=\"en\">\na \"stub\" parameter of an \"include\" SSI directive was not used,\nif empty response has 200 status code.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли проксированный или FastCGI запрос внутренне перенаправлялся\nв другой проксированный или FastCGI location,\nто в рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.8.33.<br/>\nСпасибо Yichun Zhang.\n</para>\n<para lang=\"en\">\nif a proxied or FastCGI request was internally redirected\nto another proxied or FastCGI location,\nthen a segmentation fault might occur in a worker process;\nthe bug had appeared in 0.8.33.<br/>\nThanks to Yichun Zhang.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсоединения IMAP к серверу Zimbra могло зависнуть до таймаута.<br/>\nСпасибо Alan Batie.\n</para>\n<para lang=\"en\">\nIMAP connections may hang until they timed out\nwhile talking to Zimbra server.<br/>\nThanks to Alan Batie.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.36\" date=\"2010-04-22\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module неправильно обрабатывал методы DELETE, COPY и MOVE\nдля симлинков.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module handled incorrectly the DELETE, COPY, and MOVE methods\nfor symlinks.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль SSI в подзапросах использовал закэшированные в основном запросе\nзначения переменных $query_string, $arg_... и им подобных.\n</para>\n<para lang=\"en\">\nvalues of the $query_string, $arg_..., etc. variables cached in main\nrequest were used by the SSI module in subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзначение переменной повторно экранировалось после каждого вывода\nSSI-команды echo;\nошибка появилась в 0.6.14.\n</para>\n<para lang=\"en\">\na variable value was repeatedly encoded after each\nan \"echo\" SSI-command output;\nthe bug had appeared in 0.6.14.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрабочий процесс зависал при запросе файла FIFO.<br/>\nСпасибо Vicente Aguilar и Максиму Дунину.\n</para>\n<para lang=\"en\">\na worker process hung if a FIFO file was requested.<br/>\nThanks to Vicente Aguilar and Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с OpenSSL-1.0.0 на 64-битном Linux.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nOpenSSL-1.0.0 compatibility on 64-bit Linux.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-http-cache;\nошибка появилась в 0.8.35.\n</para>\n<para lang=\"en\">\nnginx could not be built --without-http-cache;\nthe bug had appeared in 0.8.35.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.35\" date=\"2010-04-01\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь charset-фильтр работает до SSI-фильтра.\n</para>\n<para lang=\"en\">\nnow the charset filter runs before the SSI filter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива chunked_transfer_encoding.\n</para>\n<para lang=\"en\">\nthe \"chunked_transfer_encoding\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсимвол \"&amp;\" при копировании в аргументы в правилах rewrite не экранировался.\n</para>\n<para lang=\"en\">\nan \"&amp;\" character was not escaped when it was copied in arguments part\nin a rewrite rule.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог завершаться аварийно во время обработки сигнала или\nпри использовании директивы timer_resolution на платформах,\nне поддерживающих методы kqueue или eventport.<br/>\nСпасибо George Xie и Максиму Дунину.\n</para>\n<para lang=\"en\">\nnginx might be terminated abnormally\nwhile a signal processing or if the directive \"timer_resolution\" was used\non platforms which do not support kqueue or eventport notification methods.<br/>\nThanks to George Xie and Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли временные файлы и постоянное место хранения располагались на разных\nфайловых системах, то у постоянных файлов время изменения было неверным.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nif temporary files and permanent storage area resided at different\nfile systems, then permanent file modification times were incorrect.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_memcached_module мог выдавать ошибку \"memcached sent invalid\ntrailer\".<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nngx_http_memcached_module might issue the error message \"memcached sent invalid\ntrailer\".<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не мог собрать библиотеку zlib-1.2.4 из исходных текстов.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnginx could not built zlib-1.2.4 library using the library sources.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе происходил segmentation fault,\nесли перед ответом FastCGI-сервера было много вывода в stderr;\nошибка появилась в 0.8.34.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in a worker process,\nif there was large stderr output before FastCGI response;\nthe bug had appeared in 0.8.34.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.34\" date=\"2010-03-03\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не поддерживал все шифры, используемые в клиентских сертификатах.<br/>\nСпасибо Иннокентию Еникееву.\n</para>\n<para lang=\"en\">\nnginx did not support all ciphers and digests used in client certificates.<br/>\nThanks to Innocenty Enikeew.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx неправильно кэшировал FastCGI-ответы, если перед ответом было\nмного вывода в stderr.\n</para>\n<para lang=\"en\">\nnginx cached incorrectly FastCGI responses if there was large stderr output\nbefore response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не поддерживал HTTPS-рефереры.\n</para>\n<para lang=\"en\">\nnginx did not support HTTPS referrers.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows мог не находить файлы, если путь в конфигурации был задан\nв другом регистре;\nошибка появилась в 0.8.33.\n</para>\n<para lang=\"en\">\nnginx/Windows might not find file if path in configuration was given\nin other character case;\nthe bug had appeared in 0.8.33.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $date_local выдавала неверное время,\nесли использовался формат \"%s\".<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe $date_local variable has an incorrect value,\nif the \"%s\" format was used.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли ssl_session_cache не был установлен или установлен в none,\nто при проверке клиентского сертификаты могла происходить\nошибка \"session id context uninitialized\";\nошибка появилась в 0.7.1.\n</para>\n<para lang=\"en\">\nif ssl_session_cache was not set or was set to \"none\",\nthen during client certificate verify\nthe error \"session id context uninitialized\" might occur;\nthe bug had appeared in 0.7.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\ngeo-диапазон возвращал значение по умолчанию, если диапазон включал\nв себя одну и более сетей размером /16 и не начинался на границе сети\nразмером /16.\n</para>\n<para lang=\"en\">\na geo range returned default value if the range included two or more\n/16 networks and did not begin at /16 network boundary.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nблок, используемый в параметре stub в SSI-директиве include,\nвыводился с MIME-типом \"text/plain\".\n</para>\n<para lang=\"en\">\na block used in a \"stub\" parameter of an \"include\" SSI directive\nwas output with \"text/plain\" MIME type.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\n$r->sleep() не работал;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\n$r->sleep() did not work;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.33\" date=\"2010-02-01\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nтеперь nginx/Windows игнорирует пробелы в конце URI.<br/>\nСпасибо Dan Crowley, Core Security Technologies.\n</para>\n<para lang=\"en\">\nnow nginx/Windows ignores trailing spaces in URI.<br/>\nThanks to Dan Crowley, Core Security Technologies.\n</para>\n</change>\n\n<change type=\"security\">\n<para lang=\"ru\">\nтеперь nginx/Windows игнорирует короткие имена файлов.<br/>\nСпасибо Dan Crowley, Core Security Technologies.\n</para>\n<para lang=\"en\">\nnow nginx/Windows ignores short files names.<br/>\nThanks to Dan Crowley, Core Security Technologies.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь keepalive соединения после запросов POST не запрещаются для\nMSIE 7.0+.<br/>\nСпасибо Adam Lounds.\n</para>\n<para lang=\"en\">\nnow keepalive connections after POST requests are not disabled for\nMSIE 7.0+.<br/>\nThanks to Adam Lounds.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nтеперь keepalive соединения запрещены для Safari.<br/>\nСпасибо Joshua Sierles.\n</para>\n<para lang=\"en\">\nnow keepalive connections are disabled for Safari.<br/>\nThanks to Joshua Sierles.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли проксированный или FastCGI запрос внутренне перенаправлялся\nв другой проксированный или FastCGI location, то переменная\n$upstream_response_time могла иметь ненормально большое значение;\nошибка появилась в 0.8.7.\n</para>\n<para lang=\"en\">\nif a proxied or FastCGI request was internally redirected\nto another proxied or FastCGI location,\nthen $upstream_response_time variable may have abnormally large value;\nthe bug had appeared in 0.8.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault\nпри отбрасывания тела запроса;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process,\nwhile discarding a request body;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.32\" date=\"2010-01-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании кодировки UTF-8 в ngx_http_autoindex_module.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nUTF-8 encoding usage in the ngx_http_autoindex_module.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nименованные выделения в регулярных выражениях работали только для\nдвух переменных.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nregular expression named captures worked for two names only.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь в строке заголовка запроса \"Host\" используется имя \"localhost\",\nесли в директиве auth_http указан unix domain сокет.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow the \"localhost\" name is used in the \"Host\" request header line,\nif an unix domain socket is defined in the \"auth_http\" directive.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не поддерживал передачу chunk'ами для 201-ых ответов.<br/>\nСпасибо Julian Reich.\n</para>\n<para lang=\"en\">\nnginx did not support chunked transfer encoding for 201 responses.<br/>\nThanks to Julian Reich.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли директива \"expires modified\" выставляла дату в прошлом, то в строке\nзаголовка ответа \"Cache-Control\" выдавалось отрицательное число.<br/>\nСпасибо Алексею Капранову.\n</para>\n<para lang=\"en\">\nif the \"expires modified\" set date in the past, then a negative number\nwas set in the \"Cache-Control\" response header line.<br/>\nThanks to Alex Kapranoff.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.31\" date=\"2009-12-23\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива error_page может перенаправлять ответы со статусом 301 и 302.\n</para>\n<para lang=\"en\">\nnow the \"error_page\" directive may redirect the 301 and 302 responses.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $geoip_city_continent_code, $geoip_latitude и $geoip_longitude.<br/>\nСпасибо Arvind Sundararajan.\n</para>\n<para lang=\"en\">\nthe $geoip_city_continent_code, $geoip_latitude, and $geoip_longitude\nvariables.<br/>\nThanks to Arvind Sundararajan.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_image_filter_module теперь всегда удаляет\nEXIF и другие данные, если они занимают больше 5% в JPEG-файле.\n</para>\n<para lang=\"en\">\nnow the ngx_http_image_filter_module deletes always EXIF and other\napplication specific data if the data consume more than 5% of a JPEG file.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx закрывал соединение при запросе закэшированного\nответа с пустым телом.<br/>\nСпасибо Piotr Sikora.\n</para>\n<para lang=\"en\">\nnginx closed a connection if a cached response had an empty body.<br/>\nThanks to Piotr Sikora.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог не собираться gcc 4.x при использовании оптимизации -O2 и выше.<br/>\nСпасибо Максиму Дунину и Денису Латыпову.\n</para>\n<para lang=\"en\">\nnginx might not be built by gcc 4.x if the -O2 or higher optimization option\nwas used.<br/>\nThanks to Maxim Dounin and Denis F. Latypoff.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрегулярные выражения в location всегда тестировались с учётом регистра;\nошибка появилась в 0.8.25.\n</para>\n<para lang=\"en\">\nregular expressions in location were always tested in case-sensitive mode;\nthe bug had appeared in 0.8.25.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx кэшировал 304 ответ, если в заголовке проксируемого запроса\nбыла строка \"If-None-Match\".<br/>\nСпасибо Tim Dettrick и David Kostal.\n</para>\n<para lang=\"en\">\nnginx cached a 304 response if there was the \"If-None-Match\" header line\nin a proxied request.<br/>\nThanks to Tim Dettrick and David Kostal.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows пытался дважды удалить временный файл\nпри перезаписи уже существующего файла.\n</para>\n<para lang=\"en\">\nnginx/Windows tried to delete a temporary file twice\nif the file should replace an already existent file.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.30\" date=\"2009-12-15\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию размер буфера директивы large_client_header_buffers\nравен 8K.<br/>\nСпасибо Andrew Cholakian.\n</para>\n<para lang=\"en\">\nnow the default buffer size of the \"large_client_header_buffers\"\ndirective is 8K.<br/>\nThanks to Andrew Cholakian.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nфайл conf/fastcgi.conf для простых конфигураций FastCGI.\n</para>\n<para lang=\"en\">\nthe conf/fastcgi.conf for simple FastCGI configurations.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows пытался дважды переименовать временный файл\nпри перезаписи уже существующего файла.\n</para>\n<para lang=\"en\">\nnginx/Windows tried to rename a temporary file twice if the file\nshould replace an already existent file.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки double free or corruption, возникающей, если имя хоста не было найдено;\nошибка появилась в 0.8.22.<br/>\nСпасибо Константину Свисту.\n</para>\n<para lang=\"en\">\nof \"double free or corruption\" error issued if host could not be resolved;\nthe bug had appeared in 0.8.22.<br/>\nThanks to Konstantin Svist.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв использовании libatomic на некоторых платформах.<br/>\nСпасибо W-Mark Kubacki.\n</para>\n<para lang=\"en\">\nin libatomic usage on some platforms.<br/>\nThanks to W-Mark Kubacki.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.29\" date=\"2009-11-30\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь для проксируемых ответов HTTP/0.9 в лог пишется код ответа \"009\".\n</para>\n<para lang=\"en\">\nnow the \"009\" status code is written to an access log for proxied HTTP/0.9\nresponses.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы addition_types, charset_types, gzip_types, ssi_types,\nsub_filter_types и xslt_types поддерживают параметр \"*\".\n</para>\n<para lang=\"en\">\nthe \"addition_types\", \"charset_types\", \"gzip_types\", \"ssi_types\",\n\"sub_filter_types\", and \"xslt_types\" directives support an \"*\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nиспользование встроенных атомарных операций GCC 4.1+.<br/>\nСпасибо W-Mark Kubacki.\n</para>\n<para lang=\"en\">\nGCC 4.1+ built-in atomic operations usage.<br/>\nThanks to W-Mark Kubacki.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр --with-libatomic[=DIR] в configure.<br/>\nСпасибо W-Mark Kubacki.\n</para>\n<para lang=\"en\">\nthe --with-libatomic[=DIR] option in the configure.<br/>\nThanks to W-Mark Kubacki.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nlisten unix domain сокет имели ограниченные права доступа.\n</para>\n<para lang=\"en\">\nlisten unix domain socket had limited access rights.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзакэшированные ответы ответов HTTP/0.9 неправильно обрабатывались.\n</para>\n<para lang=\"en\">\ncached HTTP/0.9 responses were handled incorrectly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nименованные выделения в регулярных выражениях, заданные как \"?P&lt;...&gt;\",\nне работали в директиве server_name.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nregular expression named captures given by \"?P&lt;...&gt;\" did not work\nin a \"server_name\" directive.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.28\" date=\"2009-11-23\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-pcre;\nошибка появилась в 0.8.25.\n</para>\n<para lang=\"en\">\nnginx could not be built with the --without-pcre parameter;\nthe bug had appeared in 0.8.25.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.27\" date=\"2009-11-17\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрегулярные выражения не работали в nginx/Windows;\nошибка появилась в 0.8.25.\n</para>\n<para lang=\"en\">\nregular expressions did not work in nginx/Windows;\nthe bug had appeared in 0.8.25.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.26\" date=\"2009-11-16\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании выделений в директиве rewrite;\nошибка появилась в 0.8.25.\n</para>\n<para lang=\"en\">\nin captures usage in \"rewrite\" directive;\nthe bug had appeared in 0.8.25.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался без параметра --with-debug;\nошибка появилась в 0.8.25.\n</para>\n<para lang=\"en\">\nnginx could not be built without the --with-debug option;\nthe bug had appeared in 0.8.25.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.25\" date=\"2009-11-16\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь в лог ошибок не пишется сообщение, если переменная не найдена\nс помощью метода $r->variable().\n</para>\n<para lang=\"en\">\nnow no message is written in an error log if a variable is not found by\n$r->variable() method.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_degradation_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_degradation_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nименованные выделения в регулярных выражениях.\n</para>\n<para lang=\"en\">\nregular expression named captures.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при использовании переменных в директиве proxy_pass не требуется\nзадавать URI.\n</para>\n<para lang=\"en\">\nnow URI part is not required a \"proxy_pass\" directive if variables are used.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива msie_padding работает и для Chrome.\n</para>\n<para lang=\"en\">\nnow the \"msie_padding\" directive works for Chrome too.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе происходил segmentation fault при недостатке памяти;\nошибка появилась в 0.8.18.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in a worker process on low memory condition;\nthe bug had appeared in 0.8.18.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx передавал сжатые ответы клиентам, не поддерживающим сжатие,\nпри настройках gzip_static on и gzip_vary off;\nошибка появилась в 0.8.16.\n</para>\n<para lang=\"en\">\nnginx sent gzipped responses to clients those do not support gzip,\nif \"gzip_static on\" and \"gzip_vary off\";\nthe bug had appeared in 0.8.16.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.24\" date=\"2009-11-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx всегда добавлял строку \"Content-Encoding: gzip\" в заголовок\n304-ых ответов модуля ngx_http_gzip_static_module.\n</para>\n<para lang=\"en\">\nnginx always added \"Content-Encoding: gzip\" response header line\nin 304 responses sent by ngx_http_gzip_static_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался без параметра --with-debug;\nошибка появилась в 0.8.23.\n</para>\n<para lang=\"en\">\nnginx could not be built without the --with-debug option;\nthe bug had appeared in 0.8.23.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр \"unix:\" в директиве set_real_ip_from неправильно наследовался\nс предыдущего уровня.\n</para>\n<para lang=\"en\">\nthe \"unix:\" parameter of the \"set_real_ip_from\" directive inherited\nincorrectly from previous level.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв resolver'е при определении пустого имени.\n</para>\n<para lang=\"en\">\nin resolving empty name.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.23\" date=\"2009-11-11\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nтеперь SSL/TLS renegotiation запрещён.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow SSL/TLS renegotiation is disabled.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nlisten unix domain сокет не наследовался во время обновления без перерыва.\n</para>\n<para lang=\"en\">\nlisten unix domain socket did not inherit while online upgrade.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр \"unix:\" в директиве set_real_ip_from не работал без ещё\nодной директивы с любым IP-адресом.\n</para>\n<para lang=\"en\">\nthe \"unix:\" parameter of the \"set_real_ip_from\" directive did not without\nyet another directive with any IP address.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nsegmentation fault и зацикливания в resolver'е.\n</para>\n<para lang=\"en\">\nsegmentation fault and infinite looping in resolver.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв resolver'е.<br/>\nСпасибо Артёму Бохану.\n</para>\n<para lang=\"en\">\nin resolver.<br/>\nThanks to Artem Bokhan.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.22\" date=\"2009-11-03\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_bind, fastcgi_bind и memcached_bind.\n</para>\n<para lang=\"en\">\nthe \"proxy_bind\", \"fastcgi_bind\", and \"memcached_bind\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы access и deny поддерживают IPv6.\n</para>\n<para lang=\"en\">\nthe \"access\" and the \"deny\" directives support IPv6.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива set_real_ip_from поддерживает IPv6 адреса в заголовках запроса.\n</para>\n<para lang=\"en\">\nthe \"set_real_ip_from\" directive supports IPv6 addresses in request headers.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр \"unix:\" в директиве set_real_ip_from.\n</para>\n<para lang=\"en\">\nthe \"unix:\" parameter of the \"set_real_ip_from\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не удалял unix domain сокет после тестирования конфигурации.\n</para>\n<para lang=\"en\">\nnginx did not delete unix domain socket after configuration testing.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx удалял unix domain сокет во время обновления без перерыва.\n</para>\n<para lang=\"en\">\nnginx deleted unix domain socket while online upgrade.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nоператор \"!-x\" не работал.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe \"!-x\" operator did not work.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault\nпри использовании limit_rate в HTTPS сервере.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process,\nif limit_rate was used in HTTPS server.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри записи в лог переменной $limit_rate\nв рабочем процессе происходил segmentation fault.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process\nwhile $limit_rate logging.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли внутри блока server не было директивы listen;\nошибка появилась в 0.8.21.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in a worker process,\nif there was no \"listen\" directive in \"server\" block;\nthe bug had appeared in 0.8.21.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.21\" date=\"2009-10-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь ключ -V показывает статус поддержки TLS SNI.\n</para>\n<para lang=\"en\">\nnow the \"-V\" switch shows TLS SNI support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива listen модуля HTTP поддерживает unix domain сокеты.<br/>\nСпасибо Hongli Lai.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive of the HTTP module supports unix domain sockets.<br/>\nThanks to Hongli Lai.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр \"default_server\" в директиве listen.\n</para>\n<para lang=\"en\">\nthe \"default_server\" parameter of the \"listen\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь параметр \"default\" не обязателен для установки параметров listen-сокета.\n</para>\n<para lang=\"en\">\nnow a \"default\" parameter is not required to set listen socket options.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не поддерживал даты в 2038 году на 32-битных платформах;\n</para>\n<para lang=\"en\">\nnginx did not support dates in 2038 year on 32-bit platforms;\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nsocket leak;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.20\" date=\"2009-10-14\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию используются следующие шифры SSL: \"HIGH:!ADH:!MD5\".\n</para>\n<para lang=\"en\">\nnow default SSL ciphers are \"HIGH:!ADH:!MD5\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module не показывал последний слэш для линков\nна каталоги;\nошибка появилась в 0.7.15.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module did not show the trailing slash in links to\na directory;\nthe bug had appeared in 0.7.15.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не закрывал лог, заданный параметром конфигурации --error-log-path;\nошибка появилась в 0.7.53.\n</para>\n<para lang=\"en\">\nnginx did not close a log file set by the --error-log-path configuration option;\nthe bug had appeared in 0.7.53.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не считал запятую разделителем в строке \"Cache-Control\" в\nзаголовке ответа бэкенда.\n</para>\n<para lang=\"en\">\nnginx did not treat a comma as separator in the \"Cache-Control\" backend response\nheader line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows мог не создать временный файл, файл в кэше или файл\nс помощью директив proxy/fastcgi_store, если рабочий процесс не имел\nдостаточно прав для работы с каталогами верхнего уровня.\n</para>\n<para lang=\"en\">\nnginx/Windows might not create temporary file, a cache file, or\n\"proxy/fastcgi_store\"d file if a worker had no enough access rights\nfor top level directories.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстроки \"Set-Cookie\" и \"P3P\" в заголовке ответа FastCGI-сервера не скрывались\nпри кэшировании, если не использовались директивы fastcgi_hide_header\nс любыми параметрами.\n</para>\n<para lang=\"en\">\nthe \"Set-Cookie\" and \"P3P\" FastCGI response header lines were not hidden\nwhile caching if no \"fastcgi_hide_header\" directives were used with\nany parameters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx неверно считал размер кэша на диске.\n</para>\n<para lang=\"en\">\nnginx counted incorrectly disk cache size.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.19\" date=\"2009-10-06\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь протокол SSLv2 по умолчанию запрещён.\n</para>\n<para lang=\"en\">\nnow SSLv2 protocol is disabled by default.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию используются следующие шифры SSL:\n\"ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM\".\n</para>\n<para lang=\"en\">\nnow default SSL ciphers are \"ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива limit_req не работала;\nошибка появилась в 0.8.18.\n</para>\n<para lang=\"en\">\na \"limit_req\" directive did not work;\nthe bug had appeared in 0.8.18.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.18\" date=\"2009-10-06\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива read_ahead.\n</para>\n<para lang=\"en\">\nthe \"read_ahead\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь можно использовать несколько директив perl_modules.\n</para>\n<para lang=\"en\">\nnow several \"perl_modules\" directives may be used.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы limit_req_log_level и limit_conn_log_level.\n</para>\n<para lang=\"en\">\nthe \"limit_req_log_level\" and \"limit_conn_log_level\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь директива limit_req соответствует алгоритму leaky bucket.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow \"limit_req\" directive conforms to the leaky bucket algorithm.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не работал на Linux/sparc.<br/>\nСпасибо Marcus Ramberg.\n</para>\n<para lang=\"en\">\nnginx did not work on Linux/sparc.<br/>\nThanks to Marcus Ramberg.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx слал символ '\\0' в строке \"Location\" в заголовке в ответе на запрос\nMKCOL.<br/>\nСпасибо Xie Zhenye.\n</para>\n<para lang=\"en\">\nnginx sent '\\0' in a \"Location\" response header line on MKCOL request.<br/>\nThanks to Xie Zhenye.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nвместо кода ответа 499 в лог записывался код 0;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nzero status code was logged instead of 499 status code;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nsocket leak;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.17\" date=\"2009-09-28\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nтеперь символы \"/../\" запрещены в строке \"Destination\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnow \"/../\" are disabled in \"Destination\" request header line.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь значение переменной $host всегда в нижнем регистре.\n</para>\n<para lang=\"en\">\nnow $host variable value is always low case.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_session_id.\n</para>\n<para lang=\"en\">\nthe $ssl_session_id variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nsocket leak;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.16\" date=\"2009-09-22\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива image_filter_transparency.\n</para>\n<para lang=\"en\">\nthe \"image_filter_transparency\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"addition_types\" была неверно названа \"addtion_types\".\n</para>\n<para lang=\"en\">\n\"addition_types\" directive was incorrectly named \"addtion_types\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпорчи кэша resolver'а.<br/>\nСпасибо Matthew Dempsky.\n</para>\n<para lang=\"en\">\nresolver cache poisoning.<br/>\nThanks to Matthew Dempsky.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти в resolver'е.<br/>\nСпасибо Matthew Dempsky.\n</para>\n<para lang=\"en\">\nmemory leak in resolver.<br/>\nThanks to Matthew Dempsky.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nневерная строка запроса в переменной $request записывалась в access_log\nтолько при использовании error_log на уровне info или debug.\n</para>\n<para lang=\"en\">\ninvalid request line in $request variable was written in access_log\nonly if error_log was set to \"info\" or \"debug\" level.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв поддержке альфа-канала PNG в модуле ngx_http_image_filter_module.\n</para>\n<para lang=\"en\">\nin PNG alpha-channel support in the ngx_http_image_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx всегда добавлял строку \"Vary: Accept-Encoding\" в заголовок ответа,\nесли обе директивы gzip_static и gzip_vary были включены.\n</para>\n<para lang=\"en\">\nnginx always added \"Vary: Accept-Encoding\" response header line,\nif both \"gzip_static\" and \"gzip_vary\" were on.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв поддержке кодировки UTF-8 директивой try_files в nginx/Windows.\n</para>\n<para lang=\"en\">\nin UTF-8 encoding support by \"try_files\" directive in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании post_action;\nошибка появилась в 0.8.11.<br/>\nСпасибо Игорю Артемьеву.\n</para>\n<para lang=\"en\">\nin \"post_action\" directive usage;\nthe bug had appeared in 0.8.11.<br/>\nThanks to Igor Artemiev.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.15\" date=\"2009-09-14\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри обработке специально созданного запроса\nв рабочем процессе мог произойти segmentation fault.<br/>\nСпасибо Chris Ries.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nwhile specially crafted request handling.<br/>\nThanks to Chris Ries.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли были описаны имена .domain.tld, .sub.domain.tld и .domain-some.tld,\nто имя .sub.domain.tld попадало под маску .domain.tld.\n</para>\n<para lang=\"en\">\nif names .domain.tld, .sub.domain.tld, and .domain-some.tld were defined,\nthen the name .sub.domain.tld was matched by .domain.tld.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв поддержке прозрачности в модуле ngx_http_image_filter_module.\n</para>\n<para lang=\"en\">\nin transparency support in the ngx_http_image_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв файловом AIO.\n</para>\n<para lang=\"en\">\nin file AIO.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании X-Accel-Redirect;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nin X-Accel-Redirect usage;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании встроенного перла;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nin embedded perl module;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.14\" date=\"2009-09-07\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nустаревший закэшированный запрос мог залипнуть в состоянии \"UPDATING\".\n</para>\n<para lang=\"en\">\nan expired cached response might stick in the \"UPDATING\" state.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании error_log на уровне info или debug\nв рабочем процессе мог произойти segmentation fault.<br/>\nСпасибо Сергею Боченкову.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif error_log was set to info or debug level.<br/>\nThanks to Sergey Bochenkov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании встроенного перла;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nin embedded perl module;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива error_page не перенаправляла ошибку 413;\nошибка появилась в 0.6.10.\n</para>\n<para lang=\"en\">\nan \"error_page\" directive did not redirect a 413 error;\nthe bug had appeared in 0.6.10.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.13\" date=\"2009-08-31\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве \"aio sendfile\";\nошибка появилась в 0.8.12.\n</para>\n<para lang=\"en\">\nin the \"aio sendfile\" directive;\nthe bug had appeared in 0.8.12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался без параметра --with-file-aio на FreeBSD;\nошибка появилась в 0.8.12.\n</para>\n<para lang=\"en\">\nnginx could not be built without the --with-file-aio option on FreeBSD;\nthe bug had appeared in 0.8.12.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.12\" date=\"2009-08-31\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр sendfile в директиве aio во FreeBSD.\n</para>\n<para lang=\"en\">\nthe \"sendfile\" parameter in the \"aio\" directive on FreeBSD.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании try_files;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nin try_files;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании memcached;\nошибка появилась в 0.8.11.\n</para>\n<para lang=\"en\">\nin memcached;\nthe bug had appeared in 0.8.11.\n</para>\n</change>\n\n</changes>\n\n<changes ver=\"0.8.11\" date=\"2009-08-28\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директива \"gzip_disable msie6\" не запрещает сжатие для\n<nobr>MSIE 6.0 SV1.</nobr>\n</para>\n<para lang=\"en\">\nnow directive \"gzip_disable msie6\" does not disable gzipping for\n<nobr>MSIE 6.0 SV1.</nobr>\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка файлового AIO во FreeBSD и Linux.\n</para>\n<para lang=\"en\">\nfile AIO support on FreeBSD and Linux.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива directio_alignment.\n</para>\n<para lang=\"en\">\nthe \"directio_alignment\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.10\" date=\"2009-08-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечек памяти при использовании базы GeoIP City.\n</para>\n<para lang=\"en\">\nmemory leaks if GeoIP City database was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при копировании временных файлов в постоянное место хранения;\nошибка появилась в 0.8.9.\n</para>\n<para lang=\"en\">\nin copying temporary files to permanent storage area;\nthe bug had appeared in 0.8.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.9\" date=\"2009-08-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь стартовый загрузчик кэша работает в отдельном процесс;\nэто должно улучшить обработку больших кэшей.\n</para>\n<para lang=\"en\">\nnow the start cache loader runs in a separate process;\nthis should improve large caches handling.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь временные файлы и постоянное место хранения могут располагаться\nна разных файловых системах.\n</para>\n<para lang=\"en\">\nnow temporary files and permanent storage area may reside at\ndifferent file systems.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.8\" date=\"2009-08-10\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке заголовков ответа, разделённых в FastCGI-записях.\n</para>\n<para lang=\"en\">\nin handling FastCGI headers split in records.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли запрос обрабатывался в двух проксированных или FastCGI location'ах\nи в первом из них использовалось кэширование,\nто в рабочем процессе происходил segmentation fault;\nошибка появилась в 0.8.7.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif a request was handled in two proxied or FastCGIed locations\nand a caching was enabled in the first location;\nthe bug had appeared in 0.8.7.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.7\" date=\"2009-07-27\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nминимальная поддерживаемая версия OpenSSL&mdash;0.9.7.\n</para>\n<para lang=\"en\">\nminimum supported OpenSSL version is 0.9.7.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр ask директивы ssl_verify_client изменён на параметр optional\nи теперь он проверяет клиентский сертификат, если он был предложен.<br/>\nСпасибо Brice Figureau.\n</para>\n<para lang=\"en\">\nthe \"ask\" parameter of the \"ssl_verify_client\" directive was changed\nto the \"optional\" parameter and now it checks a client certificate if it was\noffered.<br/>\nThanks to Brice Figureau.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_client_verify.<br/>\nСпасибо Brice Figureau.\n</para>\n<para lang=\"en\">\nthe $ssl_client_verify variable.<br/>\nThanks to Brice Figureau.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_crl.<br/>\nСпасибо Brice Figureau.\n</para>\n<para lang=\"en\">\nthe \"ssl_crl\" directive.<br/>\nThanks to Brice Figureau.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр proxy директивы geo.\n</para>\n<para lang=\"en\">\nthe \"proxy\" parameter of the \"geo\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива image_filter поддерживает переменные для задания размеров.\n</para>\n<para lang=\"en\">\nthe \"image_filter\" directive supports variables for setting size.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nиспользование переменной $ssl_client_cert портило память;\nошибка появилась в 0.7.7.<br/>\nСпасибо Сергею Журавлёву.\n</para>\n<para lang=\"en\">\nthe $ssl_client_cert variable usage corrupted memory;\nthe bug had appeared in 0.7.7.<br/>\nThanks to Sergey Zhuravlev.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_pass_header и fastcgi_pass_header\" не передавали клиенту\nстроки \"X-Accel-Redirect\", \"X-Accel-Limit-Rate\", \"X-Accel-Buffering\" и\n\"X-Accel-Charset\" из заголовка ответа бэкенда.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\n\"proxy_pass_header\" and \"fastcgi_pass_header\" directives did not pass to\na client the \"X-Accel-Redirect\", \"X-Accel-Limit-Rate\", \"X-Accel-Buffering\",\nand \"X-Accel-Charset\" lines from backend response header.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке строк \"Last-Modified\" и \"Accept-Ranges\" в заголовке ответа бэкенда;\nошибка появилась в 0.7.44.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nin handling \"Last-Modified\" and \"Accept-Ranges\" backend response header lines;\nthe bug had appeared in 0.7.44.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"[alert] zero size buf\" при получении пустых ответы в подзапросах;\nошибка появилась в 0.8.5.\n</para>\n<para lang=\"en\">\nthe \"[alert] zero size buf\" error if subrequest returns an empty response;\nthe bug had appeared in 0.8.5.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.6\" date=\"2009-07-20\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_geoip_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_geoip_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nXSLT-фильтр мог выдавать ошибку \"not well formed XML document\" для\nправильного документа.<br/>\nСпасибо Kuramoto Eiji.\n</para>\n<para lang=\"en\">\nXSLT filter may fail with message \"not well formed XML document\"\nfor valid XML document.<br/>\nThanks to Kuramoto Eiji.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв MacOSX, Cygwin и nginx/Windows при проверке location'ов, заданных\nрегулярным выражением, теперь всегда делается сравнение без учёта\nрегистра символов.\n</para>\n<para lang=\"en\">\nnow in MacOSX, Cygwin, and nginx/Windows locations given by a regular\nexpression are always tested in case insensitive mode.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx/Windows игнорирует точки в конце URI.<br/>\nСпасибо Hugo Leisink.\n</para>\n<para lang=\"en\">\nnow nginx/Windows ignores trailing dots in URI.<br/>\nThanks to Hugo Leisink.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nимя файла указанного в --conf-path игнорировалось при установке;\nошибка появилась в 0.6.6.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nname of file specified in --conf-path was not honored during installation;\nthe bug had appeared in 0.6.6.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.5\" date=\"2009-07-13\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx разрешает подчёркивания в методе запроса.\n</para>\n<para lang=\"en\">\nnow nginx allows underscores in a request method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTP Basic-аутентификации на Windows\nдля неверных имени/пароля возвращалась 500-ая ошибка.\n</para>\n<para lang=\"en\">\na 500 error code was returned for invalid login/password while HTTP\nBasic authentication on Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы модуля ngx_http_perl_module не работали в подзапросах.\n</para>\n<para lang=\"en\">\nngx_http_perl_module responses did not work in subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_limit_req_module.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nin ngx_http_limit_req_module.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.4\" date=\"2009-06-22\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-http-cache;\nошибка появилась в 0.8.3.\n</para>\n<para lang=\"en\">\nnginx could not be built --without-http-cache;\nthe bug had appeared in 0.8.3.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.3\" date=\"2009-06-19\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $upstream_cache_status.\n</para>\n<para lang=\"en\">\nthe $upstream_cache_status variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на MacOSX 10.6.\n</para>\n<para lang=\"en\">\nnginx could not be built on MacOSX 10.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-http-cache;\nошибка появилась в 0.8.2.\n</para>\n<para lang=\"en\">\nnginx could not be built --without-http-cache;\nthe bug had appeared in 0.8.2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовался перехват 401 ошибки от бэкенда и бэкенд\nне возвращал строку \"WWW-Authenticate\" в заголовке ответа,\nто в рабочем процессе происходил segmentation fault.<br/>\nСпасибо Евгению Мычло.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif a backend 401 error was intercepted and the backend did not set\nthe \"WWW-Authenticate\" response header line.<br/>\nThanks to Eugene Mychlo.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.2\" date=\"2009-06-15\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво взаимодействии open_file_cache и proxy/fastcgi кэша на старте.\n</para>\n<para lang=\"en\">\nin open_file_cache and proxy/fastcgi cache interaction on start up.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nopen_file_cache мог кэшировать открытые файлы очень долго;\nошибка появилась в 0.7.4.\n</para>\n<para lang=\"en\">\nopen_file_cache might cache open file descriptors too long;\nthe bug had appeared in 0.7.4.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.1\" date=\"2009-06-08\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр updating в директивах proxy_cache_use_stale и fastcgi_cache_use_stale.\n</para>\n<para lang=\"en\">\nthe \"updating\" parameter in \"proxy_cache_use_stale\" and\n\"fastcgi_cache_use_stale\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстроки \"If-Modified-Since\", \"If-Range\" и им подобные в заголовке запроса\nклиента передавались бэкенду при кэшировании, если не использовалась\nдиректива proxy_set_header с любыми параметрами.\n</para>\n<para lang=\"en\">\nthe \"If-Modified-Since\", \"If-Range\", etc. client request header lines\nwere passed to backend while caching if no \"proxy_set_header\" directive\nwas used with any parameters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстроки \"Set-Cookie\" и \"P3P\" в заголовке ответа бэкенда не скрывались\nпри кэшировании, если не использовались директивы\nproxy_hide_header/fastcgi_hide_header с любыми параметрами.\n</para>\n<para lang=\"en\">\nthe \"Set-Cookie\" and \"P3P\" response header lines were not hidden while caching\nif no \"proxy_hide_header/fastcgi_hide_header\" directives were used with\nany parameters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_image_filter_module не понимал формат GIF87a.<br/>\nСпасибо Денису Ильиных.\n</para>\n<para lang=\"en\">\nthe ngx_http_image_filter_module did not support GIF87a format.<br/>\nThanks to Denis Ilyinyh.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Solaris 10 и более ранних;\nошибка появилась в 0.7.56.\n</para>\n<para lang=\"en\">\nnginx could not be built modules on Solaris 10 and early;\nthe bug had appeared in 0.7.56.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.8.0\" date=\"2009-06-02\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива keepalive_requests.\n</para>\n<para lang=\"en\">\nthe \"keepalive_requests\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива limit_rate_after.<br/>\nСпасибо Ivan Debnar.\n</para>\n<para lang=\"en\">\nthe \"limit_rate_after\" directive.<br/>\nThanks to Ivan Debnar.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nXSLT-фильтр не работал в подзапросах.\n</para>\n<para lang=\"en\">\nXLST filter did not work in subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nобработке относительных путей в nginx/Windows.\n</para>\n<para lang=\"en\">\nin relative paths handling in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв proxy_store, fastcgi_store, proxy_cache и fastcgi_cache в nginx/Windows.\n</para>\n<para lang=\"en\">\nin proxy_store, fastcgi_store, proxy_cache, and fastcgi_cache in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке ошибок выделения памяти.<br/>\nСпасибо Максиму Дунину и Кириллу Коринскому.\n</para>\n<para lang=\"en\">\nin memory allocation error handling.<br/>\nThanks to Maxim Dounin and Kirill A. Korinskiy.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.59\" date=\"2009-05-25\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_cache_methods и fastcgi_cache_methods.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_methods\" and \"fastcgi_cache_methods\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов;\nошибка появилась в 0.7.25.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nsocket leak;\nthe bug had appeared in 0.7.25.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменной $request_body\nв рабочем процессе происходил segmentation fault,\nесли в запросе не было тела;\nошибка появилась в 0.7.58.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\n<nobr>if a request</nobr> had no body and the $request_body\nvariable was used;<br/>\nthe bug had appeared in 0.7.58.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSL-модули могли не собираться на Solaris и Linux;\nошибка появилась в 0.7.56.\n</para>\n<para lang=\"en\">\nthe SSL modules might not built on Solaris and Linux;<br/>\nthe bug had appeared in 0.7.56.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы модуля ngx_http_xslt_filter_module не обрабатывались\nSSI-, charset- и gzip-фильтрами.\n</para>\n<para lang=\"en\">\nngx_http_xslt_filter_module responses were not handled by SSI, charset,\nand gzip filters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива charset не ставила кодировку для ответов модуля\nngx_http_gzip_static_module.\n</para>\n<para lang=\"en\">\na \"charset\" directive did not set a charset to ngx_http_gzip_static_module\nresponses.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.58\" date=\"2009-05-18\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива listen почтового прокси-сервера поддерживает IPv6.\n</para>\n<para lang=\"en\">\na \"listen\" directive of the mail proxy module supports IPv6.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива image_filter_jpeg_quality.\n</para>\n<para lang=\"en\">\nthe \"image_filter_jpeg_quality\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива client_body_in_single_buffer.\n</para>\n<para lang=\"en\">\nthe \"client_body_in_single_buffer\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $request_body.\n</para>\n<para lang=\"en\">\nthe $request_body variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_autoindex_module в ссылках на имена файлов,\nсодержащих символ \":\".\n</para>\n<para lang=\"en\">\nin ngx_http_autoindex_module in file name links\nhaving a \":\" symbol in the name.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроцедура \"make upgrade\" не работала;\nошибка появилась в 0.7.53.<br/>\nСпасибо Денису Латыпову.\n</para>\n<para lang=\"en\">\n\"make upgrade\" procedure did not work;\nthe bug had appeared in 0.7.53.<br/>\nThanks to Denis F. Latypoff.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.57\" date=\"2009-05-12\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении ошибок модуля ngx_http_image_filter_module\nв именованный location в рабочем процессе происходил floating-point fault;\nошибка появилась в 0.7.56.\n</para>\n<para lang=\"en\">\na floating-point fault occurred in worker process,\nif the ngx_http_image_filter_module errors were redirected to named location;\nthe bug had appeared in 0.7.56.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.56\" date=\"2009-05-11\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nnginx/Windows поддерживает IPv6 в директиве listen модуля HTTP.\n</para>\n<para lang=\"en\">\nnginx/Windows supports IPv6 in a \"listen\" directive of the HTTP module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_image_filter_module.\n</para>\n<para lang=\"en\">\nin ngx_http_image_filter_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.55\" date=\"2009-05-06\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметры http_XXX в директивах proxy_cache_use_stale\nи fastcgi_cache_use_stale не работали.\n</para>\n<para lang=\"en\">\nthe http_XXX parameters in \"proxy_cache_use_stale\" and\n\"fastcgi_cache_use_stale\" directives did not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nfastcgi кэш не кэшировал ответы, состоящие только из заголовка.\n</para>\n<para lang=\"en\">\nfastcgi cache did not cache header only responses.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"select() failed (9: Bad file descriptor)\" в nginx/Unix\nи \"select() failed (10038: ...)\" в nginx/Windows.\n</para>\n<para lang=\"en\">\nof \"select() failed (9: Bad file descriptor)\" error in nginx/Unix\nand \"select() failed (10038: ...)\" error in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы debug_connection\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.7.54.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif an \"debug_connection\" directive was used;\nthe bug had appeared in 0.7.54.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв сборке модуля ngx_http_image_filter_module.\n</para>\n<para lang=\"en\">\nfix ngx_http_image_filter_module building errors.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nфайлы больше 2G не передавались с использованием $r->sendfile.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe files bigger than 2G could not be transferred using $r->sendfile.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.54\" date=\"2009-05-01\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_image_filter_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_image_filter_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_ignore_headers и fastcgi_ignore_headers.\n</para>\n<para lang=\"en\">\nthe \"proxy_ignore_headers\" and \"fastcgi_ignore_headers\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменных \"open_file_cache_errors on\"\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.7.53.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif an \"open_file_cache_errors off\" directive was used;\nthe bug had appeared in 0.7.53.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"port_in_redirect off\" не работала;\nошибка появилась в 0.7.39.\n</para>\n<para lang=\"en\">\nthe \"port_in_redirect off\" directive did not work;\nthe bug had appeared in 0.7.39.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nулучшение обработки ошибок метода select.\n</para>\n<para lang=\"en\">\nimprove handling of \"select\" method errors.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"select() failed (10022: ...)\" в nginx/Windows.\n</para>\n<para lang=\"en\">\nof \"select() failed (10022: ...)\" error in nginx/Windows.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв текстовых сообщениях об ошибках в nginx/Windows;\nошибка появилась в 0.7.53.\n</para>\n<para lang=\"en\">\nin error text descriptions in nginx/Windows;\nthe bug had appeared in 0.7.53.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.53\" date=\"2009-04-27\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь лог, указанный в --error-log-path, создаётся с самого начала работы.\n</para>\n<para lang=\"en\">\nnow a log set by --error-log-path is created from the very start-up.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь ошибки и предупреждения при старте записываются в error_log\nи выводятся на stderr.\n</para>\n<para lang=\"en\">\nnow the start up errors and warnings are outputted to an error_log and stderr.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпри сборке с пустым параметром --prefix= nginx использует как префикс каталог,\nв котором он был запущен.\n</para>\n<para lang=\"en\">\nthe empty --prefix= configure parameter forces nginx to use a directory\nwhere it was run as prefix.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -p.\n</para>\n<para lang=\"en\">\nthe -p switch.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -s на Unix-платформах.\n</para>\n<para lang=\"en\">\nthe -s switch on Unix platforms.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключи -? и -h.<br/>\nСпасибо Jerome Loyet.\n</para>\n<para lang=\"en\">\nthe -? and -h switches.<br/>\nThanks to Jerome Loyet.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь ключи можно задавать в сжатой форме.\n</para>\n<para lang=\"en\">\nnow switches may be set in condensed form.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx/Windows не работал, если файл конфигурации был задан ключом -c.\n</para>\n<para lang=\"en\">\nnginx/Windows did not work if configuration file was given by the -c switch.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директив proxy_store, fastcgi_store,\nproxy_cache или fastcgi_cache временные файлы могли не удаляться.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\ntemporary files might be not removed if the \"proxy_store\", \"fastcgi_store\",\n\"proxy_cache\", or \"fastcgi_cache\" were used.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв заголовке Auth-Method запроса серверу аутентификации почтового\nпрокси-сервера передавалось неверное значение;\nошибка появилась в 0.7.34.<br/>\nСпасибо Simon Lecaille.\n</para>\n<para lang=\"en\">\nan incorrect value was passed to mail proxy authentication server\nin \"Auth-Method\" header line;\nthe bug had appeared<br/>\nin 0.7.34.<br/>\nThanks to Simon Lecaille.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри логгировании на Linux не писались текстовые описания системных ошибок;\nошибка появилась в 0.7.45.\n</para>\n<para lang=\"en\">\nsystem error text descriptions were not logged on Linux;<br/>\nthe bug had appeared in 0.7.45.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива fastcgi_cache_min_uses не работала.<br/>\nСпасибо Андрею Воробьёву.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_cache_min_uses\" directive did not work.<br/>\nThanks to Andrew Vorobyoff.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.52\" date=\"2009-04-20\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпервая бинарная версия под Windows.\n</para>\n<para lang=\"en\">\nthe first native Windows binary release.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nкорректная обработка метода HEAD при кэшировании.\n</para>\n<para lang=\"en\">\nin processing HEAD method while caching.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nкорректная обработка строк \"If-Modified-Since\", \"If-Range\" и им подобных\nв заголовке запроса клиента при кэшировании.\n</para>\n<para lang=\"en\">\nin processing the \"If-Modified-Since\", \"If-Range\", etc. client request\nheader lines while caching.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь строки \"Set-Cookie\" и \"P3P\" скрываются в заголовке ответа\nдля закэшированных ответов.\n</para>\n<para lang=\"en\">\nnow the \"Set-Cookie\" and \"P3P\" header lines are hidden in cacheable responses.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx был собран с модулем ngx_http_perl_module и perl\nподдерживал потоки, то при выходе основного процесса\nмогла выдаваться ошибка \"panic: MUTEX_LOCK\".\n</para>\n<para lang=\"en\">\nif nginx was built with the ngx_http_perl_module and with a perl which\nsupports threads, then during a master process exit\nthe message \"panic: MUTEX_LOCK\" might be issued.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-http-cache;\nошибка появилась в 0.7.48.\n</para>\n<para lang=\"en\">\nnginx could not be built --without-http-cache;\nthe bug had appeared in 0.7.48.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на платформах, отличных от i386, amd64, sparc и ppc;\nошибка появилась в 0.7.42.\n</para>\n<para lang=\"en\">\nnginx could not be built on platforms different from i386, amd64, sparc,\nand ppc;\nthe bug had appeared in 0.7.42.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.51\" date=\"2009-04-12\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива try_files поддерживает код ответа в последнем параметре.\n</para>\n<para lang=\"en\">\nthe \"try_files\" directive supports a response code in the fallback parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь в директиве return можно использовать любой код ответа.\n</para>\n<para lang=\"en\">\nnow any response code can be used in the \"return\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива error_page делала внешний редирект без строки запроса;\nошибка появилась в 0.7.44.\n</para>\n<para lang=\"en\">\nthe \"error_page\" directive made an external redirect without query string;\nthe bug had appeared in 0.7.44.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли сервера слушали на нескольких явно описанных адресах,\nто виртуальные сервера могли не работать;\nошибка появилась в 0.7.39.\n</para>\n<para lang=\"en\">\nif servers listened on several defined explicitly addresses,\nthen virtual servers might not work;\nthe bug had appeared in 0.7.39.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.50\" date=\"2009-04-06\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременные $arg_... не работали;\nошибка появилась в 0.7.49.\n</para>\n<para lang=\"en\">\nthe $arg_... variables did not work;\nthe bug had appeared in 0.7.49.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.49\" date=\"2009-04-06\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменных $arg_...\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.7.48.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif the $arg_... variables were used;\nthe bug had appeared in 0.7.48.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.48\" date=\"2009-04-06\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_cache_key.\n</para>\n<para lang=\"en\">\nthe \"proxy_cache_key\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx учитывает при кэшировании строки \"X-Accel-Expires\",\n\"Expires\" и \"Cache-Control\" в заголовке ответа бэкенда.\n</para>\n<para lang=\"en\">\nnow nginx takes into account the \"X-Accel-Expires\", \"Expires\", and\n\"Cache-Control\" header lines in a backend response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx кэширует только ответы на запросы GET.\n</para>\n<para lang=\"en\">\nnow nginx caches responses for the GET requests only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива fastcgi_cache_key не наследовалась.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_cache_key\" directive was not inherited.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременные $arg_... не работали с SSI-подзапросами.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe $arg_... variables did not work with SSI subrequests.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с библиотекой uclibc.<br/>\nСпасибо Timothy Redaelli.\n</para>\n<para lang=\"en\">\nnginx could not be built with uclibc library.<br/>\nThanks to Timothy Redaelli.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на OpenBSD;\nошибка появилась <nobr>в 0.7.46.</nobr>\n</para>\n<para lang=\"en\">\nnginx could not be built on OpenBSD;\nthe bug had <nobr>appeared in 0.7.46.</nobr>\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.47\" date=\"2009-04-01\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на FreeBSD 6 и более ранних версиях;\nошибка появилась в 0.7.46.\n</para>\n<para lang=\"en\">\nnginx could not be built on FreeBSD 6 and early versions;\nthe bug had appeared in 0.7.46.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на MacOSX;\nошибка появилась в 0.7.46.\n</para>\n<para lang=\"en\">\nnginx could not be built on MacOSX;\nthe bug had <nobr>appeared in 0.7.46.</nobr>\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовался параметр max_size, то cache manager мог удалить весь кэш;\nошибка появилась в 0.7.46.\n</para>\n<para lang=\"en\">\nif the \"max_size\" parameter was set, then the cache manager might purge\na whole cache;\nthe bug had appeared in 0.7.46.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли директивы proxy_cache/fastcgi_cache\nи proxy_cache_valid/ fastcgi_cache_valid не были заданы на одном уровне;\nошибка появилась в 0.7.46.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif the \"proxy_cache\"/\"fastcgi_cache\" and\nthe \"proxy_cache_valid\"/ \"fastcgi_cache_valid\" were set on different levels;\nthe bug had appeared in 0.7.46.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault\nпри перенаправлении запроса проксированному или FastCGI-серверу\nс помощью error_page или try_files;\nошибка появилась в 0.7.44.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif a request was redirected to a proxied or FastCGI server via\nerror_page or try_files;\nthe bug had appeared in 0.7.44.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.46\" date=\"2009-03-30\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nархив предыдущего релиза был неверным.\n</para>\n<para lang=\"en\">\nthe previous release tarball was incorrect.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.45\" date=\"2009-03-30\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директивы proxy_cache и proxy_cache_valid можно задавать\nна разных уровнях.\n</para>\n<para lang=\"en\">\nnow the \"proxy_cache\" and the \"proxy_cache_valid\" directives can be set on\ndifferent levels.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр clean_time в директиве proxy_cache_path удалён.\n</para>\n<para lang=\"en\">\nthe \"clean_time\" parameter of the \"proxy_cache_path\" directive is canceled.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр max_size в директиве proxy_cache_path.\n</para>\n<para lang=\"en\">\nthe \"max_size\" parameter of the \"proxy_cache_path\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпредварительная поддержка кэширования в модуле ngx_http_fastcgi_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_fastcgi_module preliminary cache support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при ошибках выделения в разделяемой памяти в логе указываются\nназвания директивы и зоны.\n</para>\n<para lang=\"en\">\nnow on shared memory allocation errors directive and zone names are logged.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"add_header last-modified ''\" не удаляла в заголовке ответа\nстроку \"Last-Modified\";\nошибка появилась в 0.7.44.\n</para>\n<para lang=\"en\">\nthe directive \"add_header last-modified ''\" did not delete a \"Last-Modified\"\nresponse header line;\nthe bug had appeared in 0.7.44.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве auth_basic_user_file не работал относительный путь,\nзаданный строкой без переменных;\nошибка появилась в 0.7.44.<br/>\nСпасибо Jerome Loyet.\n</para>\n<para lang=\"en\">\na relative path in the \"auth_basic_user_file\" directive given without variables\ndid not work;\nthe bug had appeared in 0.7.44.<br/>\nThanks to Jerome Loyet.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве alias, заданной переменными\nбез ссылок на выделения в регулярных выражениях;\nошибка появилась в 0.7.42.\n</para>\n<para lang=\"en\">\nin an \"alias\" directive given using variables\nwithout references to captures of regular expressions;\nthe bug had appeared in 0.7.42.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.44\" date=\"2009-03-23\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпредварительная поддержка кэширования в модуле ngx_http_proxy_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_proxy_module preliminary cache support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр --with-pcre в configure.\n</para>\n<para lang=\"en\">\nthe --with-pcre option in the configure.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива try_files может быть использована на уровне server.\n</para>\n<para lang=\"en\">\nthe \"try_files\" directive is now allowed on the server block level.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива try_files неправильно обрабатывала строку запроса в последнем\nпараметре.\n</para>\n<para lang=\"en\">\nthe \"try_files\" directive handled incorrectly a query string\nin a fallback parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива try_files могла неверно тестировать каталоги.\n</para>\n<para lang=\"en\">\nthe \"try_files\" directive might test incorrectly directories.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли для пары адрес:порт описан только один сервер, то выделения\nв регулярных выражениях в директиве server_name не работали.\n</para>\n<para lang=\"en\">\nif there was a single server for given address:port pair,\nthen captures in regular expressions in a \"server_name\" directive did not work.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.43\" date=\"2009-03-18\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзапрос обрабатывался неверно, если директива root использовала переменные;\nошибка появилась в 0.7.42.\n</para>\n<para lang=\"en\">\na request was handled incorrectly, if a \"root\" directive used variables;\nthe bug had appeared in 0.7.42.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли сервер слушал на адресах типа \"*\", то значение переменной $server_addr\nбыло \"0.0.0.0\";\nошибка появилась в 0.7.36.\n</para>\n<para lang=\"en\">\nif a server listened on wildcard address, then the $server_addr variable\nvalue was \"0.0.0.0\";\nthe bug had appeared in 0.7.36.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.42\" date=\"2009-03-16\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nошибка \"Invalid argument\", возвращаемая setsockopt(TCP_NODELAY) на Solaris,\nтеперь игнорируется.\n</para>\n<para lang=\"en\">\nnow the \"Invalid argument\" error returned by setsockopt(TCP_NODELAY) on Solaris,\nis ignored.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпри отсутствии файла, указанного в директиве auth_basic_user_file,\nтеперь возвращается ошибка 403 вместо 500.\n</para>\n<para lang=\"en\">\nnow if a file specified in a \"auth_basic_user_file\" directive is absent,\nthen the 403 error is returned instead of the 500 one.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива auth_basic_user_file поддерживает переменные.\n<br/>\nСпасибо Кириллу Коринскому.\n</para>\n<para lang=\"en\">\nthe \"auth_basic_user_file\" directive supports variables.<br/>\nThanks to Kirill A. Korinskiy.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива listen поддерживает параметр ipv6only.<br/>\nСпасибо Zhang Hua.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive supports the \"ipv6only\" parameter.\n<br/>\nThanks to Zhang Hua.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве alias со ссылками на выделения в регулярных выражениях;\nошибка появилась в 0.7.40.\n</para>\n<para lang=\"en\">\nin an \"alias\" directive with references to captures of regular expressions;\nthe bug had appeared in 0.7.40.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с Tru64 UNIX.<br/>\nСпасибо Dustin Marquess.\n</para>\n<para lang=\"en\">\ncompatibility with Tru64 UNIX.<br/>\nThanks to Dustin Marquess.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался без библиотеки PCRE;\nошибка появилась в 0.7.41.\n</para>\n<para lang=\"en\">\nnginx could not be built without PCRE library;\nthe bug had appeared in 0.7.41.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.41\" date=\"2009-03-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли в server_name или location были выделения в регулярных выражениях;\nошибка появилась в 0.7.40.<br/>\nСпасибо Владимиру Сопоту.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif a \"server_name\" or a \"location\" directives had captures\nin regular expressions;\nthe issue had appeared in 0.7.40.<br/>\nThanks to Vladimir Sopot.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.40\" date=\"2009-03-09\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива location поддерживает выделения в регулярных выражениях.\n</para>\n<para lang=\"en\">\nthe \"location\" directive supports captures in regular expressions.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу alias с ссылками на выделения в регулярных выражениях\nможно использовать внутри location'а, заданного регулярным выражением\nс выделениями.\n</para>\n<para lang=\"en\">\nan \"alias\" directive with capture references may be used inside\na location given by a regular expression with captures.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_name поддерживает выделения в регулярных выражениях.\n</para>\n<para lang=\"en\">\nthe \"server_name\" directive supports captures in regular expressions.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module не показывал последний слэш для каталогов\nна файловой системе XFS;\nошибка появилась в 0.7.15.<br/>\nСпасибо Дмитрию Кузьменко.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module did not show the trailing slash in directories\non XFS filesystem;\nthe issue had appeared in 0.7.15.<br/>\nThanks to Dmitry Kuzmenko.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.39\" date=\"2009-03-02\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включённом сжатии большие ответы с использованием SSI могли зависать;\nошибка появилась в 0.7.28.<br/>\nСпасибо Артёму Бохану.\n</para>\n<para lang=\"en\">\nlarge response with SSI might hang, if gzipping was enabled;\nthe bug had appeared in 0.7.28.<br/>\nThanks to Artem Bokhan.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании коротких статических вариантов в директиве try_files\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process,\nif short static variants are used in a \"try_files\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.38\" date=\"2009-02-23\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nлоггирование ошибок аутентификации.\n</para>\n<para lang=\"en\">\nauthentication failures logging.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nимя/пароль, заданные в auth_basic_user_file, игнорировались после нечётного\nчисла пустых строк.<br/>\nСпасибо Александру Загребину.\n</para>\n<para lang=\"en\">\nname/password in auth_basic_user_file were ignored after odd number\nof empty lines.<br/>\nThanks to Alexander Zagrebin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании длинного пути в unix domain сокете\nв главном процессе происходил segmentation fault;\nошибка появилась в 0.7.36.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in a master process,\nif long path was used in unix domain socket;\nthe bug had appeared in 0.7.36.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.37\" date=\"2009-02-21\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы, использующие upstream'ы, не работали;\nошибка появилась в 0.7.36.\n</para>\n<para lang=\"en\">\ndirectives using upstreams did not work;\nthe bug had appeared in 0.7.36.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.36\" date=\"2009-02-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпредварительная поддержка IPv6;\nдиректива listen модуля HTTP поддерживает IPv6.\n</para>\n<para lang=\"en\">\na preliminary IPv6 support;\nthe \"listen\" directive of the HTTP module supports IPv6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $ancient_browser не работала для браузеров, заданных\nдирективами modern_browser.\n</para>\n<para lang=\"en\">\nthe $ancient_browser variable did not work for browsers\npreset by a \"modern_browser\" directives.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.35\" date=\"2009-02-16\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива ssl_engine не использовала SSL-акселератор\nдля асимметричных шифров.<br/>\nСпасибо Marcin Gozdalik.\n</para>\n<para lang=\"en\">\na \"ssl_engine\" directive did not use a SSL-accelerator\nfor asymmetric ciphers.<br/>\nThanks to Marcin Gozdalik.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива try_files выставляла MIME-type, исходя из расширения\nпервоначального запроса.\n</para>\n<para lang=\"en\">\na \"try_files\" directive set MIME type depending on an\noriginal request extension.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директивах server_name, valid_referers и map\nнеправильно обрабатывались имена вида \"*domain.tld\",\nесли использовались маски вида \".domain.tld\" и \".subdomain.domain.tld\";\nошибка появилась в 0.7.9.\n</para>\n<para lang=\"en\">\n\"*domain.tld\" names were handled incorrectly in\n\"server_name\", \"valid_referers\", and \"map\" directives,\nif \".domain.tld\" and \".subdomain.domain.tld\" wildcards were used;\n<nobr>the bug had</nobr> appeared in 0.7.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.34\" date=\"2009-02-10\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр off в директиве if_modified_since.\n</para>\n<para lang=\"en\">\nthe \"off\" parameter of the \"if_modified_since\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь после команды XCLIENT nginx посылает команду HELO/EHLO.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow nginx sends an HELO/EHLO command after a XCLIENT command.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка Microsoft-специфичного режима\n<nobr>\"AUTH LOGIN with User Name\"</nobr>\nв почтовом прокси-сервере.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nMicrosoft specific \"AUTH LOGIN with User Name\" mode support\nin mail proxy server.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве rewrite, возвращающей редирект, старые аргументы присоединялись\nк новым через символ \"?\" вместо \"&amp;\";<br/>\nошибка появилась в 0.1.18.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nin a redirect rewrite directive original arguments were concatenated with\nnew arguments by a \"?\" rather than an \"&amp;\";<br/>\nthe bug had appeared in 0.1.18.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на AIX.\n</para>\n<para lang=\"en\">\nnginx could not be built on AIX.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.33\" date=\"2009-02-02\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли на запрос с телом возвращался редирект, то ответ мог быть двойным\nпри использовании методов epoll или rtsig.<br/>\nСпасибо Eden Li.\n</para>\n<para lang=\"en\">\na double response might be returned if the epoll or rtsig methods are used\nand a redirect was returned to a request with body.<br/>\nThanks to Eden Li.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдля некоторых типов редиректов в переменной $sent_http_location\nбыло пустое значение.\n</para>\n<para lang=\"en\">\nthe $sent_http_location variable was empty for some redirects types.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы resolver в SMTP прокси-сервере\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif \"resolver\" directive was used in SMTP proxy.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.32\" date=\"2009-01-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь в директиве try_files можно явно указать проверку каталога.\n</para>\n<para lang=\"en\">\nnow a directory existence testing can be set explicitly\nin the \"try_files\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nfastcgi_store не всегда сохранял файлы.\n</para>\n<para lang=\"en\">\nfastcgi_store stored files not always.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв гео-диапазонах.\n</para>\n<para lang=\"en\">\nin geo ranges.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки выделения больших блоков в разделяемой памяти,\nесли nginx был собран без отладки.<br/>\nСпасибо Андрею Квасову.\n</para>\n<para lang=\"en\">\nin shared memory allocations if nginx was built without debugging.<br/>\nThanks to Andrey Kvasov.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.31\" date=\"2009-01-19\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директива try_files проверяет только файлы, игнорируя каталоги.\n</para>\n<para lang=\"en\">\nnow the \"try_files\" directive tests files only and ignores directories.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_split_path_info.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_split_path_info\" directive.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nИсправления в поддержке строки \"Expect\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nBugfixes in an \"Expect\" request header line support.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nИсправления в гео-диапазонах.\n</para>\n<para lang=\"en\">\nBugfixes in geo ranges.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри отсутствии ответа ngx_http_memcached_module возвращал\nв теле ответа строку \"END\" вместо 404-ой страницы по умолчанию;\nошибка появилась в 0.7.18.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nin a miss case ngx_http_memcached_module returned the \"END\" line\nas response body instead of default 404 page body;\nthe bug had appeared in 0.7.18.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании SMTP nginx выдавал сообщение\n<nobr>\"250 2.0.0 OK\"</nobr> вместо \"235 2.0.0 OK\";\nошибка появилась в 0.7.22.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nwhile SMTP proxying nginx issued message\n\"250 2.0.0 OK\" instead of \"235 2.0.0 OK\";\nthe bug had appeared in 0.7.22.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n\n<changes ver=\"0.7.30\" date=\"2008-12-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе происходил segmentation fault,\nесли в директивах fastcgi_pass или proxy_pass\nиспользовались переменные и имя хоста должно было резолвиться;\nошибка появилась в 0.7.29.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif variables were used in the \"fastcgi_pass\" or \"proxy_pass\" directives\nand host name must be resolved;\nthe bug had appeared in 0.7.29.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.29\" date=\"2008-12-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы fastcgi_pass и proxy_pass не поддерживали переменные\nпри использовании unix domain сокетов.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_pass\" and \"proxy_pass\" directives did not support\nvariables if unix domain sockets were used.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nИсправления в обработке подзапросов;\nошибки появились в 0.7.25.\n</para>\n<para lang=\"en\">\nBugfixes in subrequest processing;\nthe bugs had appeared in 0.7.25.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответ \"100 Continue\" выдавался для запросов версии HTTP/1.0;<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na \"100 Continue\" response was issued for HTTP/1.0 requests;<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв выделении памяти в модуле ngx_http_gzip_filter_module под Cygwin.\n</para>\n<para lang=\"en\">\nin memory allocation in the ngx_http_gzip_filter_module on Cygwin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.28\" date=\"2008-12-22\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nв выделении памяти в модуле ngx_http_gzip_filter_module.\n</para>\n<para lang=\"en\">\nin memory allocation in the ngx_http_gzip_filter_module.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nзначения по умолчанию для директивы gzip_buffers изменены с 4 4k/8k\nна 32 4k или 16 8k.\n</para>\n<para lang=\"en\">\nthe default \"gzip_buffers\" directive values have been changed\nto 32 4k or 16 8k from 4 4k/8k.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.27\" date=\"2008-12-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива try_files.\n</para>\n<para lang=\"en\">\nthe \"try_files\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_pass поддерживает переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"fastcgi_pass\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива geo может брать адрес из переменной.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nnow the $geo variable may get an address from a variable.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь модификатор location'а можно указывать без пробела перед названием.\n</para>\n<para lang=\"en\">\nnow a location's modifier may be used without space before name.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $upstream_response_length.\n</para>\n<para lang=\"en\">\nthe $upstream_response_length variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь директива add_header не добавляет пустое значение.\n</para>\n<para lang=\"en\">\nnow a \"add_header\" directive does not add an empty value.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри запросе файла нулевой длины nginx закрывал соединение, ничего не передав;\nошибка появилась в 0.7.25.\n</para>\n<para lang=\"en\">\nif zero length static file was requested, then nginx just closed connection;\nthe bug had appeared in 0.7.25.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод MOVE не мог перемещать файл в несуществующий каталог.\n</para>\n<para lang=\"en\">\na MOVE method could not move file in non-existent directory.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в сервере не был описан ни один именованный location,\nно такой location использовался в директиве error_page,\nто в рабочем процессе происходил segmentation fault.<br/>\nСпасибо Сергею Боченкову.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif no one named location was defined in server,\nbut some one was used in an error_page directive.<br/>\nThanks to Sergey Bochenkov.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.26\" date=\"2008-12-08\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке подзапросов;\nошибка появилась в 0.7.25.\n</para>\n<para lang=\"en\">\nin subrequest processing;\nthe bug had appeared in 0.7.25.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.25\" date=\"2008-12-08\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nв обработке подзапросов.\n</para>\n<para lang=\"en\">\nin subrequest processing.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь разрешаются POST'ы без строки \"Content-Length\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnow POSTs without \"Content-Length\" header line are allowed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь директивы limit_req и limit_conn указывают причину запрета запроса.\n</para>\n<para lang=\"en\">\nnow the \"limit_req\" and \"limit_conn\" directives log a prohibition reason.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв параметре delete директивы geo.\n</para>\n<para lang=\"en\">\nin the \"delete\" parameter of the \"geo\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.24\" date=\"2008-12-01\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива if_modified_since.\n</para>\n<para lang=\"en\">\nthe \"if_modified_since\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не обрабатывал ответ FastCGI-сервера,\nесли перед ответом сервер передавал много сообщений в stderr.\n</para>\n<para lang=\"en\">\nnginx did not process a FastCGI server response,\nif the server send too many messages to stderr before response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременные \"$cookie_...\" не работали в SSI and в перловом модуле.\n</para>\n<para lang=\"en\">\nthe \"$cookie_...\" variables did not work in the SSI and the perl module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.23\" date=\"2008-11-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры delete и ranges в директиве geo.\n</para>\n<para lang=\"en\">\nthe \"delete\" and \"ranges\" parameters in the \"geo\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nускорение загрузки geo-базы с большим числом значений.\n</para>\n<para lang=\"en\">\nspeeding up loading of geo base with large number of values.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nуменьшение памяти, необходимой для загрузки geo-базы.\n</para>\n<para lang=\"en\">\ndecrease of memory required for geo base load.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.22\" date=\"2008-11-20\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр none в директиве smtp_auth.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe \"none\" parameter in the \"smtp_auth\" directive.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные \"$cookie_...\".\n</para>\n<para lang=\"en\">\nthe \"$cookie_...\" variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива directio не работала с файловой системой XFS.\n</para>\n<para lang=\"en\">\nthe \"directio\" directive did not work in XFS filesystem.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nresolver не понимал большие DNS-ответы.<br/>\nСпасибо Zyb.\n</para>\n<para lang=\"en\">\nthe resolver did not understand big DNS responses.<br/>\nThanks to Zyb.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.21\" date=\"2008-11-11\">\n\n<change>\n<para lang=\"ru\">\nИзменения в модуле ngx_http_limit_req_module.\n</para>\n<para lang=\"en\">\nChanges in the ngx_http_limit_req_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка EXSLT в модуле ngx_http_xslt_module.<br/>\nСпасибо Денису Латыпову.\n</para>\n<para lang=\"en\">\nthe EXSLT support in the ngx_http_xslt_module.<br/>\nThanks to Denis F. Latypoff.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nсовместимость с glibc 2.3.<br/>\nСпасибо Eric Benson и Максиму Дунину.\n</para>\n<para lang=\"en\">\ncompatibility with glibc 2.3.<br/>\nThanks to Eric Benson and Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не запускался на MacOSX 10.4 и более ранних;\nошибка появилась в 0.7.6.\n</para>\n<para lang=\"en\">\nnginx could not run on MacOSX 10.4 and earlier;\nthe bug had appeared in 0.7.6.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.20\" date=\"2008-11-10\">\n\n<change>\n<para lang=\"ru\">\nИзменения в модуле ngx_http_gzip_filter_module.\n</para>\n<para lang=\"en\">\nChanges in the ngx_http_gzip_filter_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_limit_req_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_limit_req_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна платформах sparc и ppc рабочие процессы могли выходить по сигналу SIGBUS;\nошибка появилась в 0.7.3.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nworker processes might exit on a SIGBUS signal on sparc and ppc platforms;\nthe bug had appeared in 0.7.3.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы вида \"proxy_pass http://host/some:uri\" не работали;\nошибка появилась в 0.7.12.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass http://host/some:uri\" directives did not work;\nthe bug had appeared in 0.7.12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTPS запросы могли завершаться с ошибкой \"bad write retry\".\n</para>\n<para lang=\"en\">\nin HTTPS mode requests might fail with the \"bad write retry\" error.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_secure_link_module не работал внутри location'ов\nс именами меньше 3 символов.\n</para>\n<para lang=\"en\">\nthe ngx_http_secure_link_module did not work inside locations,\nwhose names are less than 3 characters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $server_addr могла не иметь значения.\n</para>\n<para lang=\"en\">\n$server_addr variable might have no value.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.19\" date=\"2008-10-13\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nобновление номера версии.\n</para>\n<para lang=\"en\">\nversion number update.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.18\" date=\"2008-10-13\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива underscores_in_headers;\nтеперь nginx по умолчанию не разрешает подчёркивания в именах строк\nв заголовке запроса клиента.\n</para>\n<para lang=\"en\">\nthe \"underscores_in_headers\" directive;\nnow nginx does not allows underscores in a client request header line names.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_secure_link_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_secure_link_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива real_ip_header поддерживает любой заголовок.\n</para>\n<para lang=\"en\">\nthe \"real_ip_header\" directive supports any header.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива log_subrequest.\n</para>\n<para lang=\"en\">\nthe \"log_subrequest\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $realpath_root.\n</para>\n<para lang=\"en\">\nthe $realpath_root variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры http_502 и http_504 в директиве proxy_next_upstream.\n</para>\n<para lang=\"en\">\nthe \"http_502\" and \"http_504\" parameters of the \"proxy_next_upstream\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр http_503 в директивах proxy_next_upstream или fastcgi_next_upstream\nне работал.\n</para>\n<para lang=\"en\">\nthe \"http_503\" parameter of the \"proxy_next_upstream\" or\n\"fastcgi_next_upstream\" directives did not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx мог выдавать строку \"Transfer-Encoding: chunked\" для запросов  HEAD.\n</para>\n<para lang=\"en\">\nnginx might send a \"Transfer-Encoding: chunked\" header line for HEAD requests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь accept-лимит зависит от числа worker_connections.\n</para>\n<para lang=\"en\">\nnow accept threshold depends on worker_connections.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.17\" date=\"2008-09-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива directio теперь работает на Linux.\n</para>\n<para lang=\"en\">\nnow the \"directio\" directive works on Linux.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $pid.\n</para>\n<para lang=\"en\">\nthe $pid variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nоптимизация directio, появившаяся в 0.7.15, не работала при использовании\nopen_file_cache.\n</para>\n<para lang=\"en\">\nthe \"directio\" optimization that had appeared in 0.7.15 did not work with\nopen_file_cache.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\naccess_log с переменными не работал на Linux;\nошибка появилась в 0.7.7.\n</para>\n<para lang=\"en\">\nthe \"access_log\" with variables did not work on Linux;\nthe bug had appeared in 0.7.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_charset_module не понимал название кодировки в кавычках,\nполученное от бэкенда.\n</para>\n<para lang=\"en\">\nthe ngx_http_charset_module did not understand quoted charset name\nreceived from backend.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.16\" date=\"2008-09-08\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на 64-битных платформах;\nошибка появилась в 0.7.15.\n</para>\n<para lang=\"en\">\nnginx could not be built on 64-bit platforms;\nthe bug had appeared in 0.7.15.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.15\" date=\"2008-09-08\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_random_index_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_random_index_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива directio оптимизирована для запросов файлов, начинающихся\nс произвольной позиции.\n</para>\n<para lang=\"en\">\nthe \"directio\" directive has been optimized for file requests starting\nfrom arbitrary position.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива directio при необходимости запрещает использование sendfile.\n</para>\n<para lang=\"en\">\nthe \"directio\" directive turns off sendfile if it is necessary.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx разрешает подчёркивания в именах строк в заголовке запроса клиента.\n</para>\n<para lang=\"en\">\nnow nginx allows underscores in a client request header line names.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.14\" date=\"2008-09-01\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директивы ssl_certificate и ssl_certificate_key не имеют\nзначений по умолчанию.\n</para>\n<para lang=\"en\">\nnow the ssl_certificate and ssl_certificate_key directives have no\ndefault values.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива listen поддерживает параметр ssl.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive supports the \"ssl\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при переконфигурации nginx учитывает изменение временной зоны\nна FreeBSD и Linux.\n</para>\n<para lang=\"en\">\nnow nginx takes into account a time zone change while reconfiguration\non FreeBSD and Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметры директивы listen, такие как backlog, rcvbuf и прочие,\nне устанавливались, если сервером по умолчанию был не первый сервер.\n</para>\n<para lang=\"en\">\nthe \"listen\" directive parameters such as \"backlog\", \"rcvbuf\", etc.\nwere not set, if a default server was not the first one.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании в качестве аргументов части URI, выделенного с помощью\nдирективы rewrite, эти аргументы не экранировались.\n</para>\n<para lang=\"en\">\nif URI part captured by a \"rewrite\" directive was used as a query string,\nthen the query string was not escaped.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nулучшения тестирования правильности конфигурационного файла.\n</para>\n<para lang=\"en\">\nconfiguration file validity test improvements.\n</para>\n</change>\n\n\n</changes>\n\n\n<changes ver=\"0.7.13\" date=\"2008-08-26\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Linux и Solaris;\nошибка появилась в 0.7.12.\n</para>\n<para lang=\"en\">\nnginx could not be built on Linux and Solaris;\nthe bug had appeared in 0.7.12.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.12\" date=\"2008-08-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_name поддерживает пустое имя \"\".\n</para>\n<para lang=\"en\">\nthe \"server_name\" directive supports empty name \"\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива gzip_disable поддерживает специальную маску msie6.\n</para>\n<para lang=\"en\">\nthe \"gzip_disable\" directive supports special \"msie6\" mask.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании параметра max_fails=0 в upstream'е с несколькими\nсерверами рабочий процесс выходил по сигналу SIGFPE.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nif the \"max_fails=0\" parameter was used in upstream with several servers,\nthen a worker process exited on a SIGFPE signal.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении запроса с помощью директивы error_page\nтерялось тело запроса.\n</para>\n<para lang=\"en\">\na request body was dropped while redirection via an \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении запроса с методом HEAD с помощью директивы error_page\nвозвращался полный ответ.\n</para>\n<para lang=\"en\">\na full response was returned for request method HEAD\nwhile redirection via an \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод $r->header_in() не возвращал значения строк \"Host\", \"User-Agent\",\nи \"Connection\" из заголовка запроса;\nошибка появилась в 0.7.0.\n</para>\n<para lang=\"en\">\nthe $r->header_in() method did not return value of the \"Host\",\n\"User-Agent\", and \"Connection\" request header lines;\nthe bug had appeared in 0.7.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.11\" date=\"2008-08-18\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь ngx_http_charset_module по умолчанию не работает MIME-типом text/css.\n</para>\n<para lang=\"en\">\nnow ngx_http_charset_module does not work by default with text/css MIME type.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx возвращает код 405 для метода POST при запросе статического\nфайла, только если файл существует.\n</para>\n<para lang=\"en\">\nnow nginx returns the 405 status code for POST method requesting a static file\nonly if the file exists.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_ssl_session_reuse.\n</para>\n<para lang=\"en\">\nthe \"proxy_ssl_session_reuse\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле перенаправления запроса с помощью \"X-Accel-Redirect\"\nдиректива proxy_pass без URI могла использовать оригинальный запрос.\n</para>\n<para lang=\"en\">\na \"proxy_pass\" directive without URI part might use original request\nafter the \"X-Accel-Redirect\" redirection was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли у каталога были права доступа только на поиск файлов\nи первый индексный файл отсутствовал, то nginx возвращал ошибку 500.\n</para>\n<para lang=\"en\">\nif a directory has search only rights and the first index file was absent,\nthen nginx returned the 500 status code.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибок во вложенных location'ах;\nошибки появились в 0.7.1.\n</para>\n<para lang=\"en\">\nin inclusive locations;\nthe bugs had appeared in 0.7.1.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.10\" date=\"2008-08-13\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибок в директивах addition_types, charset_types,\ngzip_types, ssi_types, sub_filter_types и xslt_types;\nошибки появились в 0.7.9.\n</para>\n<para lang=\"en\">\nin the \"addition_types\", \"charset_types\",\n\"gzip_types\", \"ssi_types\", \"sub_filter_types\", and \"xslt_types\" directives;\nthe bugs had appeared in 0.7.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрекурсивной error_page для 500 ошибки.\n</para>\n<para lang=\"en\">\nof recursive error_page for 500 status code.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь модуль ngx_http_realip_module устанавливает адрес не для\nвсего keepalive соединения, а для каждого запроса по этому соединению.\n</para>\n<para lang=\"en\">\nnow the ngx_http_realip_module sets address not for whole keepalive connection,\nbut for each request passed via the connection.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.9\" date=\"2008-08-12\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь ngx_http_charset_module по умолчанию работает со следующими MIME-типами:\ntext/html, text/css, text/xml, text/plain, text/vnd.wap.wml,\napplication/x-javascript и application/rss+xml.\n</para>\n<para lang=\"en\">\nnow ngx_http_charset_module works by default with following MIME types:\ntext/html, text/css, text/xml, text/plain, text/vnd.wap.wml,\napplication/x-javascript, and application/rss+xml.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы charset_types и addition_types.\n</para>\n<para lang=\"en\">\nthe \"charset_types\" and \"addition_types\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директивы gzip_types, ssi_types и sub_filter_types используют хэш.\n</para>\n<para lang=\"en\">\nnow the \"gzip_types\", \"ssi_types\", and \"sub_filter_types\" directives use hash.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_cpp_test_module.\n</para>\n<para lang=\"en\">\nthe ngx_cpp_test_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива expires поддерживает суточное время.\n</para>\n<para lang=\"en\">\nthe \"expires\" directive supports daily time.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nулучшения и исправления в модуле ngx_http_xslt_module.<br/>\nСпасибо Денису Латыпову и Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe ngx_http_xslt_module improvements and bug fixing.<br/>\nThanks to Denis F. Latypoff and Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива log_not_found не работала при поиске индексных файлов.\n</para>\n<para lang=\"en\">\nthe \"log_not_found\" directive did not work for index files tests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nHTTPS-соединения могли зависнуть,\nесли использовались методы kqueue, epoll, rtsig или eventport;\nошибка появилась в 0.7.7.\n</para>\n<para lang=\"en\">\nHTTPS connections might hang,\nif kqueue, epoll, rtsig, or eventport methods were used;\nthe bug had appeared in 0.7.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директивах server_name, valid_referers и map\nиспользовалась маска вида \"*.domain.tld\" и при этом полное имя\nвида \"domain.tld\" не было описано, то это имя попадало под маску;\nошибка появилась в 0.3.18.\n</para>\n<para lang=\"en\">\nif the \"server_name\", \"valid_referers\", and \"map\" directives used\nan \"*.domain.tld\" wildcard and exact name \"domain.tld\" was not set,\nthen the exact name was matched by the wildcard;\nthe bug had appeared in 0.3.18.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.8\" date=\"2008-08-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_xslt_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_xslt_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные \"$arg_...\".\n</para>\n<para lang=\"en\">\nthe \"$arg_...\" variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка directio в Solaris.<br/>\nСпасибо Ivan Debnar.\n</para>\n<para lang=\"en\">\nSolaris directio support.<br/>\nThanks to Ivan Debnar.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь, если FastCGI-сервер присылает строку \"Location\" в заголовке ответа\nбез строки статуса, то nginx использует код статуса 302.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow if FastCGI server sends a \"Location\" header line without status line,\nthen nginx uses 302 status code.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.7\" date=\"2008-07-30\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь ошибка EAGAIN при вызове connect() не считается временной.\n</para>\n<para lang=\"en\">\nnow the EAGAIN error returned by connect() is not considered as temporary error.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nзначением переменной $ssl_client_cert теперь является сертификат,\nперед каждой строкой которого, кроме первой, вставляется символ табуляции;\nнеизменённый сертификат доступен через переменную $ssl_client_raw_cert.\n</para>\n<para lang=\"en\">\nnow the $ssl_client_cert variable value is a certificate with TAB character\nintended before each line except first one;\nan unchanged certificate is available in the $ssl_client_raw_cert variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр ask директивы ssl_verify_client.\n</para>\n<para lang=\"en\">\nthe \"ask\" parameter in the \"ssl_verify_client\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nулучшения в обработке byte-range.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nbyte-range processing improvements.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива directio.<br/>\nСпасибо Jiang Hong.\n</para>\n<para lang=\"en\">\nthe \"directio\" directive.<br/>\nThanks to Jiang Hong.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка sendfile() в MacOSX 10.5.\n</para>\n<para lang=\"en\">\nMacOSX 10.5 sendfile() support.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв MacOSX и Cygwin при проверке location'ов теперь делается сравнение\nбез учёта регистра символов;\nоднако, сравнение ограничено только однобайтными locale'ями.\n</para>\n<para lang=\"en\">\nnow in MacOSX and Cygwin locations are tested in case insensitive mode;\nhowever, the compare is provided by single-byte locales only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсоединения почтового прокси-сервера зависали в режиме SSL,\nесли использовались методы select, poll или /dev/poll.\n</para>\n<para lang=\"en\">\nmail proxy SSL connections hanged,\nif select, poll, or /dev/poll methods were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании кодировки UTF-8 в ngx_http_autoindex_module.\n</para>\n<para lang=\"en\">\nUTF-8 encoding usage in the ngx_http_autoindex_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.6\" date=\"2008-07-07\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь при использовании переменных в директиве access_log\nвсегда проверяется существовании root'а для запроса.\n</para>\n<para lang=\"en\">\nnow if variables are used in the \"access_log\" directive\na request root existence is always tested.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_flv_module не поддерживал несколько значений в\nаргументах запроса.\n</para>\n<para lang=\"en\">\nthe ngx_http_flv_module did not support several values in a query string.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.5\" date=\"2008-07-01\">\n\n<change>\n<para lang=\"ru\">\nИсправления в поддержке переменных в директиве access_log;\nошибки появились в 0.7.4.\n</para>\n<para lang=\"en\">\nBugfixes in variables support in the \"access_log\" directive;\nthe bugs had appeared in 0.7.4.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-http_gzip_module;\nошибка появилась в 0.7.3.<br/>\nСпасибо Кириллу Коринскому.\n</para>\n<para lang=\"en\">\nnginx could not be built --without-http_gzip_module;\nthe bug had appeared in 0.7.3.<br/>\nThanks to Kirill A. Korinskiy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри совместном использовании sub_filter и SSI\nответы могли передаваться неверно.\n</para>\n<para lang=\"en\">\nif sub_filter and SSI were used together, then responses might\nwere transferred incorrectly.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.4\" date=\"2008-06-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива access_log поддерживает переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"access_log\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива open_log_file_cache.\n</para>\n<para lang=\"en\">\nthe \"open_log_file_cache\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -g.\n</para>\n<para lang=\"en\">\nthe -g switch.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка строки \"Expect\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nthe \"Expect\" request header line support.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nбольшие включения в SSI могли передавались не полностью.\n</para>\n<para lang=\"en\">\nlarge SSI inclusions might be truncated.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.3\" date=\"2008-06-23\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nMIME-тип для расширения rss изменён на \"application/rss+xml\".\n</para>\n<para lang=\"en\">\nthe \"rss\" extension MIME type has been changed to \"application/rss+xml\".\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директива \"gzip_vary on\" выдаёт строку\n<nobr>\"Vary: Accept-Encoding\"</nobr>\nв заголовке ответа и для несжатых ответов.\n</para>\n<para lang=\"en\">\nnow the \"gzip_vary\" directive turned on issues\na <nobr>\"Vary: Accept-Encoding\"</nobr>\nheader line for uncompressed responses too.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при использовании протокола \"https://\" в директиве rewrite\nавтоматически делается редирект.\n</para>\n<para lang=\"en\">\nnow the \"rewrite\" directive does a redirect automatically\nif the \"https://\" protocol is used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_pass не работала с протоколом HTTPS;\nошибка появилась в 0.6.9.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\" directive did not work with the HTTPS protocol;\nthe bug had appeared in 0.6.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.2\" date=\"2008-06-16\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx поддерживает шифры с обменом EDH-ключами.\n</para>\n<para lang=\"en\">\nnow nginx supports EDH key exchange ciphers.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_dhparam.\n</para>\n<para lang=\"en\">\nthe \"ssl_dhparam\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_client_cert.<br/>\nСпасибо Manlio Perillo.\n</para>\n<para lang=\"en\">\nthe $ssl_client_cert variable.<br/>\nThanks to Manlio Perillo.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле изменения URI с помощью директивы rewrite nginx не искал новый location;\nошибка появилась в 0.7.1.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nafter changing URI via a \"rewrite\" directive nginx did not search\na new location;\nthe bug had appeared in 0.7.1.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался без библиотеки PCRE;\nошибка появилась в 0.7.1.\n</para>\n<para lang=\"en\">\nnginx could not be built without PCRE library;\nthe bug had appeared in 0.7.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри редиректе запроса к каталогу с добавлением слэша nginx\nне добавлял аргументы из оригинального запроса.\n</para>\n<para lang=\"en\">\nwhen a request to a directory was redirected with the slash added,\nnginx dropped a query string from the original request.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.1\" date=\"2008-05-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь поиск location'а делается с помощью дерева.\n</para>\n<para lang=\"en\">\nnow locations are searched in a tree.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива optimize_server_names упразднена в связи с появлением\nдирективы server_name_in_redirect.\n</para>\n<para lang=\"en\">\nthe \"optimize_server_names\" directive was canceled\ndue to the \"server_name_in_redirect\" directive introduction.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nнекоторые давно устаревшие директивы больше не поддерживаются.\n</para>\n<para lang=\"en\">\nsome long deprecated directives are not supported anymore.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр \"none\" в директиве ssl_session_cache;\nтеперь этот параметр используется по умолчанию.<br/>\nСпасибо Rob Mueller.\n</para>\n<para lang=\"en\">\nthe \"none\" parameter in the \"ssl_session_cache\" directive;\nnow this is default parameter.<br/>\nThanks to Rob Mueller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрабочие процессы могли не реагировать на сигналы переконфигурации\nи ротации логов.\n</para>\n<para lang=\"en\">\nworker processes might not catch reconfiguration and log rotation signals.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на последних Fedora 9 Linux.<br/>\nСпасибо Roxis.\n</para>\n<para lang=\"en\">\nnginx could not be built on latest Fedora 9 Linux.<br/>\nThanks to Roxis.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.7.0\" date=\"2008-05-19\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь символы 0x00-0x1F, '\"' и '\\' в access_log записываются в виде \\xXX.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow the 0x00-0x1F, '\"' and '\\' characters are escaped as \\xXX in an\naccess_log.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx разрешает несколько строк \"Host\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnow nginx allows several \"Host\" request header line.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива expires поддерживает флаг modified.\n</para>\n<para lang=\"en\">\nthe \"modified\" flag in the \"expires\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $uid_got и $uid_set можно использовать на любой стадии обработки\nзапроса.\n</para>\n<para lang=\"en\">\nthe $uid_got and $uid_set variables may be used at any request processing stage.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $hostname.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nthe $hostname variable.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка DESTDIR.<br/>\nСпасибо Todd A. Fisher и Andras Voroskoi.\n</para>\n<para lang=\"en\">\nDESTDIR support.<br/>\nThanks to Todd A. Fisher and Andras Voroskoi.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании keepalive на Linux\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process on Linux,\nif keepalive was enabled.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.31\" date=\"2008-05-12\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не обрабатывал ответ FastCGI-сервера, если строка заголовка ответа была\nв конце записи FastCGI;\nошибка появилась в 0.6.2.<br/>\nСпасибо Сергею Серову.\n</para>\n<para lang=\"en\">\nnginx did not process FastCGI response\nif header was at the end of FastCGI record;\nthe bug had appeared in 0.6.2.<br/>\nThanks to Sergey Serov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри удалении файла и использовании директивы open_file_cache_errors off\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process if a file was deleted\nand the \"open_file_cache_errors\" directive was off.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.30\" date=\"2008-04-29\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь, если маске, заданной в директиве include, не соответствует\nни один файл, то nginx не выдаёт ошибку.\n</para>\n<para lang=\"en\">\nnow if an \"include\" directive pattern does not match any file,\nthen nginx does not issue an error.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь время в директивах можно задавать без пробела, например, \"1h50m\".\n</para>\n<para lang=\"en\">\nnow the time in directives may be specified without spaces,\nfor example, \"1h50m\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечек памяти, если директива ssl_verify_client имела значение on.<br/>\nСпасибо Chavelle Vincent.\n</para>\n<para lang=\"en\">\nmemory leaks if the \"ssl_verify_client\" directive was on.<br/>\nThanks to Chavelle Vincent.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива sub_filter могла вставлять заменяемый текст в вывод.\n</para>\n<para lang=\"en\">\nthe \"sub_filter\" directive might set text to change into output.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива error_page не воспринимала параметры в перенаправляемом URI.\n</para>\n<para lang=\"en\">\nthe \"error_page\" directive did not take into account arguments in\nredirected URI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь при сборке с Cygwin nginx всегда открывает файлы в бинарном режиме.\n</para>\n<para lang=\"en\">\nnow nginx always opens files in binary mode under Cygwin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под OpenBSD;\nошибка появилась в 0.6.15.\n</para>\n<para lang=\"en\">\nnginx could not be built on OpenBSD;\nthe bug had appeared in 0.6.15.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.29\" date=\"2008-03-18\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_google_perftools_module.\n</para>\n<para lang=\"en\">\nthe ngx_google_perftools_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module не собирался на 64-битных платформах;\nошибка появилась в 0.6.27.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module could not be built on 64-bit platforms;\nthe bug had appeared in 0.6.27.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.28\" date=\"2008-03-13\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод rtsig не собирался;\nошибка появилась в 0.6.27.\n</para>\n<para lang=\"en\">\nthe rtsig method could not be built;\nthe bug had appeared in 0.6.27.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.27\" date=\"2008-03-12\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь на Linux 2.6.18+ по умолчанию не собирается метод rtsig.\n</para>\n<para lang=\"en\">\nnow by default the rtsig method is not built on <nobr>Linux 2.6.18+.</nobr>\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при перенаправлении запроса в именованный location с помощью\nдирективы error_page метод запроса не изменяется.\n</para>\n<para lang=\"en\">\nnow a request method is not changed while redirection to a named location\nvia an \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы resolver и resolver_timeout в SMTP прокси-сервере.\n</para>\n<para lang=\"en\">\nthe \"resolver\" and \"resolver_timeout\" directives in SMTP proxy.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива post_action поддерживает именованные location'ы.\n</para>\n<para lang=\"en\">\nthe \"post_action\" directive supports named locations.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении запроса из location'а c обработчиком proxy, FastCGI\nили memcached в именованный location со статическим обработчиком\nв рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif a request was redirected from proxy, FastCGI, or memcached location\nto static named locations.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nбраузеры не повторяли SSL handshake, если при первом handshake\nне оказалось правильного клиентского сертификата.\n<br/>\nСпасибо Александру Инюхину.\n</para>\n<para lang=\"en\">\nbrowsers did not repeat SSL handshake if there is no valid client certificate\nin first handshake.\n<br/>\nThanks to Alexander V. Inyukhin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении ошибок 495-497 с помощью директивы error_page\nбез изменения кода ошибки nginx пытался выделить очень много памяти.\n</para>\n<para lang=\"en\">\nif response code 495-497 was redirected via an \"error_page\" directive\nwithout code change, then nginx tried to allocate too many memory.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти в долгоживущих небуфферизированных соединениях.\n</para>\n<para lang=\"en\">\nmemory leak in long-lived non buffered connections.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки памяти в resolver'е.\n</para>\n<para lang=\"en\">\nmemory leak in resolver.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении запроса из location'а c обработчиком proxy\nв другой location с обработчиком proxy\nв рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif a request was redirected from proxy, FastCGI, or memcached location\nto static named locations.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки в кэшировании переменных $proxy_host и $proxy_port.<br/>\nСпасибо Сергею Боченкову.\n</para>\n<para lang=\"en\">\nin the $proxy_host and $proxy_port variables caching.<br/>\nThanks to Sergey Bochenkov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива proxy_pass с переменными использовала порт, описанной в другой\nдирективе proxy_pass без переменных, но с таким же именем хоста.<br/>\nСпасибо Сергею Боченкову.\n</para>\n<para lang=\"en\">\na \"proxy_pass\" directive with variables used incorrectly the same port\nas in another \"proxy_pass\" directive with the same host name\nand without variables.<br/>\nThanks to Sergey Bochenkov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво время переконфигурации на некоторых 64-битном платформах в лог\nзаписывался alert \"sendmsg() failed (9: Bad file descriptor)\".\n</para>\n<para lang=\"en\">\nan alert \"sendmsg() failed (9: Bad file descriptor)\" on some 64-bit platforms\nwhile reconfiguration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри повторном использовании в SSI пустого block'а в качестве заглушки\nв рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif empty stub block was used second time in SSI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при копировании части URI, содержащего экранированные символы,\nв аргументы.\n</para>\n<para lang=\"en\">\nin copying URI part contained escaped symbols into arguments.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.26\" date=\"2008-02-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_store и fastcgi_store не проверяли длину ответа.\n</para>\n<para lang=\"en\">\nthe \"proxy_store\" and \"fastcgi_store\" directives did not check\na response length.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании большого значения в директиве expires\nв рабочем процессе происходил segmentation fault.<br/>\nСпасибо Joaquin Cuenca Abela.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif big value was used in a \"expires\" directive.<br/>\nThanks to Joaquin Cuenca Abela.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx неверно определял длину строки кэша на <nobr>Pentium 4.</nobr><br/>\nСпасибо Геннадию Махомеду.\n</para>\n<para lang=\"en\">\nnginx incorrectly detected cache line size on Pentium 4.<br/>\nThanks to Gena Makhomed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв проксированных подзапросах и подзапросах к FastCGI-серверу\nвместо метода GET использовался оригинальный метод клиента.\n</para>\n<para lang=\"en\">\nin proxied or FastCGI subrequests a client original method was used\ninstead of the GET method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов в режиме HTTPS при использовании отложенного accept'а.<br/>\nСпасибо Ben Maurer.\n</para>\n<para lang=\"en\">\nsocket leak in HTTPS mode if deferred accept was used.<br/>\nThanks to Ben Maurer.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx выдавал ошибочное сообщение \"SSL_shutdown() failed (SSL: )\";\nошибка появилась в 0.6.23.\n</para>\n<para lang=\"en\">\nnginx issued the bogus error message \"SSL_shutdown() failed (SSL: )\";\nthe bug had appeared in 0.6.23.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTPS запросы могли завершаться с ошибкой \"bad write retry\";\nошибка появилась в 0.6.23.\n</para>\n<para lang=\"en\">\nin HTTPS mode requests might fail with the \"bad write retry\" error;\nthe bug had appeared in 0.6.23.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.25\" date=\"2008-01-08\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nвместо специального параметра \"*\" в директиве server_name теперь\nиспользуется директива server_name_in_redirect.\n</para>\n<para lang=\"en\">\nnow the \"server_name_in_redirect\" directive is used instead of\nthe \"server_name\" directive's special \"*\" parameter.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв качестве основного имени в директиве server_name теперь\nможно использовать имена с масками и регулярными выражениями.\n</para>\n<para lang=\"en\">\nnow wildcard and regex names can be used as main name in\na \"server_name\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива satisfy_any заменена директивой satisfy.\n</para>\n<para lang=\"en\">\nthe \"satisfy_any\" directive was replaced by the \"satisfy\" directive.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпосле переконфигурации старые рабочие процесс могли сильно нагружать процессор\nпри запуске под Linux OpenVZ.\n</para>\n<para lang=\"en\">\nold worker processes might hog CPU after reconfiguration if they was run\nunder Linux OpenVZ.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива min_delete_depth.\n</para>\n<para lang=\"en\">\nthe \"min_delete_depth\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметоды COPY и MOVE не работали с одиночными файлами.\n</para>\n<para lang=\"en\">\nthe COPY and MOVE methods did not work with single files.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_gzip_static_module не позволял работать модулю\nngx_http_dav_module;\nошибка появилась в 0.6.23.\n</para>\n<para lang=\"en\">\nthe ngx_http_gzip_static_module did not allow the ngx_http_dav_module to work;\nthe bug had appeared in 0.6.23.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов в режиме HTTPS при использовании отложенного accept'а.<br/>\nСпасибо Ben Maurer.\n</para>\n<para lang=\"en\">\nsocket leak in HTTPS mode if deferred accept was used.<br/>\nThanks to Ben Maurer.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался без библиотеки PCRE;\nошибка появилась в 0.6.23.\n</para>\n<para lang=\"en\">\nnginx could not be built without PCRE library;\nthe bug had appeared in 0.6.23.\n</para>\n</change>\n\n</changes>\n\n<changes ver=\"0.6.24\" date=\"2007-12-27\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTPS в рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.6.23.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process if HTTPS was used;\nthe bug had appeared in 0.6.23.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.23\" date=\"2007-12-27\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр \"off\" в директиве ssl_session_cache;\nтеперь этот параметр используется по умолчанию.\n</para>\n<para lang=\"en\">\nthe \"off\" parameter in the \"ssl_session_cache\" directive;\nnow this is default parameter.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива open_file_cache_retest переименована в open_file_cache_valid.\n</para>\n<para lang=\"en\">\nthe \"open_file_cache_retest\" directive was renamed\nto the \"open_file_cache_valid\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива open_file_cache_min_uses.\n</para>\n<para lang=\"en\">\nthe \"open_file_cache_min_uses\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_gzip_static_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_gzip_static_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива gzip_disable.\n</para>\n<para lang=\"en\">\nthe \"gzip_disable\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу memcached_pass можно использовать внутри блока if.\n</para>\n<para lang=\"en\">\nthe \"memcached_pass\" directive may be used inside the \"if\" block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли внутри одного location'а использовались директивы \"memcached_pass\" и \"if\",\nто в рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif the \"memcached_pass\" and \"if\" directives were used in the same location.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли при использовании директивы satisfy_any on\" были заданы директивы\nне всех модулей доступа, то заданные директивы не проверялись.\n</para>\n<para lang=\"en\">\nif a \"satisfy_any on\" directive was used and not all access and auth modules\ndirectives were set, then other given access and auth directives\nwere not tested;\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметры, заданные регулярным выражением в директиве valid_referers,\nне наследовалась с предыдущего уровня.\n</para>\n<para lang=\"en\">\nregex parameters in a \"valid_referers\" directive were not inherited\nfrom previous level.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива post_action не работала, если запрос завершался с кодом 499.\n</para>\n<para lang=\"en\">\na \"post_action\" directive did run if a request was completed\nwith 499 status code.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nоптимизация использования 16K буфера для SSL-соединения.<br/>\nСпасибо Ben Maurer.\n</para>\n<para lang=\"en\">\noptimization of 16K buffer usage in a SSL connection.<br/>\nThanks to Ben Maurer.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSTARTTLS в режиме SMTP не работал.<br/>\nСпасибо Олегу Мотиенко.\n</para>\n<para lang=\"en\">\nthe STARTTLS in SMTP mode did not work.<br/>\nThanks to Oleg Motienko.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании HTTPS запросы могли завершаться с ошибкой \"bad write retry\";\nошибка появилась в 0.5.13.\n</para>\n<para lang=\"en\">\nin HTTPS mode requests might fail with the \"bad write retry\" error;\nthe bug had appeared in 0.5.13.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.22\" date=\"2007-12-19\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь все методы модуля ngx_http_perl_module\nвозвращают значения, скопированные в память, выделенную perl'ом.\n</para>\n<para lang=\"en\">\nnow all ngx_http_perl_module methods return values copied to perl's\nallocated memory.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx был собран с модулем ngx_http_perl_module,\nиспользовался perl до версии 5.8.6 и perl поддерживал потоки,\nто во время переконфигурации основной процесс аварийно выходил;\nошибка появилась в 0.5.9.<br/>\nСпасибо Борису Жмурову.\n</para>\n<para lang=\"en\">\nif nginx was built with ngx_http_perl_module,\nthe perl before 5.8.6 was used, and perl supported threads,\nthen during reconfiguration the master process aborted;\nthe bug had appeared in 0.5.9.<br/>\nThanks to Boris Zhmurov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв методы модуля ngx_http_perl_module\nмогли передаваться неверные результаты выделения в регулярных выражениях.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module methods may get invalid values of the regex captures.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли метод $r->has_request_body() вызывался для запроса,\nу которого небольшое тело запроса было уже полностью получено,\nто в рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process,\nif the $r->has_request_body() method was called for a request\nwhose small request body was already received.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nlarge_client_header_buffers не освобождались перед переходом в состояние\nkeep-alive.<br/>\nСпасибо Олександру Штепе.\n</para>\n<para lang=\"en\">\nlarge_client_header_buffers did not freed before going to keep-alive state.<br/>\nThanks to Olexander Shtepa.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв переменной $upstream_addr не записывался последний адрес;\nошибка появилась в 0.6.18.\n</para>\n<para lang=\"en\">\nthe last address was missed in the $upstream_addr variable;\nthe bug had appeared in 0.6.18.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива fastcgi_catch_stderr не возвращала ошибку;\nтеперь она возвращает ошибку 502, которую можно направить на следующий сервер\nс помощью \"fastcgi_next_upstream invalid_header\".\n</para>\n<para lang=\"en\">\nthe \"fastcgi_catch_stderr\" directive did return error code;\nnow it returns 502 code, that can be rerouted to a next server using\nthe \"fastcgi_next_upstream invalid_header\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы fastcgi_catch_stderr\nв основном процессе происходил segmentation fault;\nошибка появилась в 0.6.10.<br/>\nСпасибо Manlio Perillo.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in master process\nif the \"fastcgi_catch_stderr\" directive was used;\nthe bug had appeared in 0.6.10.<br/>\nThanks to Manlio Perillo.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.21\" date=\"2007-12-03\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nесли в значениях переменных директивы proxy_pass используются\nтолько IP-адреса, то указывать resolver не нужно.\n</para>\n<para lang=\"en\">\nif variable values used in a \"proxy_pass\" directive contain IP-addresses only,\nthen a \"resolver\" directive is not mandatory.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы proxy_pass c URI-частью\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.6.19.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif a \"proxy_pass\" directive with URI-part was used;\nthe bug had appeared in 0.6.19.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли resolver использовался на платформах, не поддерживающих метод kqueue,\nто nginx выдавал alert \"name is out of response\".<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nif resolver was used on platform that does not support kqueue,\nthen nginx issued an alert \"name is out of response\".<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nПри использовании переменной $server_protocol в FastCGI-параметрах\nи запросе, длина которого была близка к значению директивы\nclient_header_buffer_size,\nnginx выдавал alert \"fastcgi: the request record is too big\".\n</para>\n<para lang=\"en\">\nif the $server_protocol was used in FastCGI parameters\nand a request line length was near to the \"client_header_buffer_size\"\ndirective value,\nthen nginx issued an alert \"fastcgi: the request record is too big\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри обычном запросе версии HTTP/0.9 к HTTPS серверу nginx возвращал\nобычный ответ.\n</para>\n<para lang=\"en\">\nif a plain text HTTP/0.9 version request was made to HTTPS server,\nthen nginx returned usual response.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.20\" date=\"2007-11-28\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы proxy_pass c URI-частью\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.6.19.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif a \"proxy_pass\" directive with URI-part was used;\nthe bug had appeared in 0.6.19.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.19\" date=\"2007-11-27\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nверсия 0.6.18 не собиралась.\n</para>\n<para lang=\"en\">\nthe 0.6.18 version could not be built.\n</para>\n</change>\n\n</changes>\n\n<changes ver=\"0.6.18\" date=\"2007-11-27\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь модуль ngx_http_userid_module в поле куки с номером процесса\nдобавляет микросекунды на время старта.\n</para>\n<para lang=\"en\">\nnow the ngx_http_userid_module adds start time microseconds\nto the cookie field contains a pid value.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв error_log теперь записывается полная строка запроса вместо только URI.\n</para>\n<para lang=\"en\">\nnow the full request line instead of URI only is written to error_log.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_pass поддерживает переменные.\n</para>\n<para lang=\"en\">\nvariables support in the \"proxy_pass\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы resolver и resolver_timeout.\n</para>\n<para lang=\"en\">\nthe \"resolver\" and \"resolver_timeout\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь директива \"add_header last-modified ''\" удаляет в заголовке ответа\nстроку \"Last-Modified\".\n</para>\n<para lang=\"en\">\nnow the directive \"add_header last-modified ''\" deletes a \"Last-Modified\"\nresponse header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива limit_rate не позволяла передавать на полной скорости,\nдаже если был указан очень большой лимит.\n</para>\n<para lang=\"en\">\nthe \"limit_rate\" directive did not allow to use full throughput,\neven if limit value was very high.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.17\" date=\"2007-11-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка строки \"If-Range\" в заголовке запроса.<br/>\nСпасибо Александру Инюхину.\n</para>\n<para lang=\"en\">\nthe \"If-Range\" request header line support.<br/>\nThanks to Alexander V. Inyukhin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы msie_refresh повторно экранировались\nуже экранированные символы;\nошибка появилась в 0.6.4.\n</para>\n<para lang=\"en\">\nURL double escaping in a redirect of the \"msie_refresh\" directive;\nthe bug had appeared in 0.6.4.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива autoindex не работала при использовании \"alias /\".\n</para>\n<para lang=\"en\">\nthe \"autoindex\" directive did not work with the \"alias /\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании подзапросов\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process if subrequests were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SSL и gzip большие ответы могли передаваться не полностью.\n</para>\n<para lang=\"en\">\nthe big responses may be transferred truncated if SSL and gzip were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли ответ проксированного сервера был версии HTTP/0.9,\nто переменная $status была равна 0.\n</para>\n<para lang=\"en\">\nthe $status variable was equal to 0 if a proxied server returned response\nin HTTP/0.9 version.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.16\" date=\"2007-10-29\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь на Linux используется uname(2) вместо procfs.<br/>\nСпасибо Илье Новикову.\n</para>\n<para lang=\"en\">\nnow the uname(2) is used on Linux instead of procfs.<br/>\nThanks to Ilya Novikov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве error_page использовался символ \"?\", то он экранировался\nпри проксировании запроса;\nошибка появилась в 0.6.11.\n</para>\n<para lang=\"en\">\nif the \"?\" character was in a \"error_page\" directive, then it was escaped\nin a proxied request;\nthe bug had appeared in 0.6.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с mget.\n</para>\n<para lang=\"en\">\ncompatibility with mget.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.15\" date=\"2007-10-22\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nсовместимость с Cygwin.<br/>\nСпасибо Владимиру Кутакову.\n</para>\n<para lang=\"en\">\nCygwin compatibility.<br/>\nThanks to Vladimir Kutakov.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива merge_slashes.\n</para>\n<para lang=\"en\">\nthe \"merge_slashes\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива gzip_vary.\n</para>\n<para lang=\"en\">\nthe \"gzip_vary\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_tokens.\n</para>\n<para lang=\"en\">\nthe \"server_tokens\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не раскодировал URI в команде SSI include.\n</para>\n<para lang=\"en\">\nnginx did not unescape URI in the \"include\" SSI command.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменной в директивах charset или source_charset\nна старте или во время переконфигурации происходил segmentation fault,\n</para>\n<para lang=\"en\">\nthe segmentation fault was occurred on start or while reconfiguration\nif variable was used in the \"charset\" or \"source_charset\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx возвращал ошибку 400 на запросы вида\n<nobr>\"GET http://www.domain.com HTTP/1.0\"</nobr>.<br/>\nСпасибо James Oakley.\n</para>\n<para lang=\"en\">\nnginx returned the 400 response on requests like\n<nobr>\"GET http://www.domain.com HTTP/1.0\"</nobr>.<br/>\nThanks to James Oakley.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле перенаправления запроса с телом запроса с помощью директивы\nerror_page nginx пытался снова прочитать тело запроса;\nошибка появилась в 0.6.7.\n</para>\n<para lang=\"en\">\nif request with request body was redirected using the \"error_page\" directive,\nthen nginx tried to read the request body again;\nthe bug had appeared in 0.6.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе происходил segmentation fault, если у сервера,\nобрабатывающему запрос, не был явно определён server_name;\nошибка появилась в 0.6.7.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process\nif no server_name was explicitly defined for server processing request;\nthe bug had appeared in 0.6.7.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.14\" date=\"2007-10-15\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь по умолчанию команда SSI echo использует кодирование entity.\n</para>\n<para lang=\"en\">\nnow by default the \"echo\" SSI command uses entity encoding.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр encoding в команде SSI echo.\n</para>\n<para lang=\"en\">\nthe \"encoding\" parameter in the \"echo\" SSI command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу access_log можно использовать внутри блока limit_except.\n</para>\n<para lang=\"en\">\nthe \"access_log\" directive may be used inside the \"limit_except\" block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли все сервера апстрима оказывались недоступными,\nто до восстановления работоспособности\nу всех серверов вес становился равным одному;\nошибка появилась в 0.6.6.\n</para>\n<para lang=\"en\">\nif all upstream servers were failed, then all servers had got weight\nthe was equal one until servers became alive;\nthe bug had appeared in 0.6.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменных $date_local и $date_gmt вне модуля\nngx_http_ssi_filter_module в рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process\nif $date_local and $date_gmt were used outside the ngx_http_ssi_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании включённом отладочном логе\nв рабочем процессе мог произойти segmentation fault.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif debug log was enabled.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nngx_http_memcached_module не устанавливал $upstream_response_time.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nngx_http_memcached_module did not set $upstream_response_time.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрабочий процесс мог зациклиться при использовании memcached.\n</para>\n<para lang=\"en\">\na worker process may got caught in an endless loop, if the memcached was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx распознавал параметры \"close\" и \"keep-alive\" в строке \"Connection\"\nв заголовке запроса только, если они были в нижнем регистре;\nошибка появилась в 0.6.11.\n</para>\n<para lang=\"en\">\nnginx supported low case only \"close\" and \"keep-alive\" values\nin the \"Connection\" request header line;\nthe bug had appeared in 0.6.11.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nsub_filter не работал с пустой строкой замены.\n</para>\n<para lang=\"en\">\nsub_filter did not work with empty substitution.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв парсинге sub_filter.\n</para>\n<para lang=\"en\">\nin sub_filter parsing.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.13\" date=\"2007-09-24\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не закрывал файл каталога для запроса HEAD,\nесли использовался autoindex<br/>\nСпасибо Arkadiusz Patyk.\n</para>\n<para lang=\"en\">\nnginx did not close directory file on HEAD request if autoindex was used.<br/>\nThanks to Arkadiusz Patyk.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.12\" date=\"2007-09-21\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпочтовый прокси-сервер разделён на три модуля: pop3, imap и smtp.\n</para>\n<para lang=\"en\">\nmail proxy was split on three modules: pop3, imap and smtp.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры конфигурации --without-mail_pop3_module,\n--without-mail_imap_module и --without-mail_smtp_module.\n</para>\n<para lang=\"en\">\nthe --without-mail_pop3_module, --without-mail_imap_module,\nand --without-mail_smtp_module configuration parameters.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы smtp_greeting_delay и smtp_client_buffer модуля ngx_mail_smtp_module.\n</para>\n<para lang=\"en\">\nthe \"smtp_greeting_delay\" and \"smtp_client_buffer\" directives\nof the ngx_mail_smtp_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nwildcard в конце имени сервера не работали;\nошибка появилась в 0.6.9.\n</para>\n<para lang=\"en\">\nthe trailing wildcards did not work;\nthe bug had appeared in 0.6.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании разделяемой библиотеки PCRE,\nрасположенной в нестандартном месте, nginx не запускался на Solaris.\n</para>\n<para lang=\"en\">\nnginx could not start on Solaris if the shared PCRE library located\nin non-standard place was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_hide_header и fastcgi_hide_header не скрывали\nстроки заголовка ответа с именем больше 32 символов.<br/>\nСпасибо Manlio Perillo.\n</para>\n<para lang=\"en\">\nthe \"proxy_hide_header\" and \"fastcgi_hide_header\" directives did not\nhide response header lines whose name was longer than 32 characters.<br/>\nThanks to Manlio Perillo.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.11\" date=\"2007-09-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсчётчик активных соединений всегда рос при использовании почтового\nпрокси-сервера.\n</para>\n<para lang=\"en\">\nactive connection counter always increased if mail proxy was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли бэкенд возвращал только заголовок ответа при небуферизированном\nпроксировании, то nginx закрывал соединение с бэкендом по таймауту.\n</para>\n<para lang=\"en\">\nif backend returned response header only using non-buffered proxy,\nthen nginx closed backend connection on timeout.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не поддерживал несколько строк \"Connection\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnginx did not support several \"Connection\" request header lines.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в сервере апстрима был задан max_fails, то после первой же неудачной\nпопытки вес сервера навсегда становился равным одному;\nошибка появилась в 0.6.6.\n</para>\n<para lang=\"en\">\nif the \"max_fails\" was set for upstream server, then after first\nfailure server weight was always one;\nthe bug had appeared in 0.6.6.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.10\" date=\"2007-09-03\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы open_file_cache, open_file_cache_retest и open_file_cache_errors.\n</para>\n<para lang=\"en\">\nthe \"open_file_cache\", \"open_file_cache_retest\", and \"open_file_cache_errors\"\ndirectives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nутечки сокетов;\nошибка появилась в 0.6.7.\n</para>\n<para lang=\"en\">\nsocket leak;\nthe bug had appeared in 0.6.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nВ строку заголовка ответа \"Content-Type\", указанную в методе\n$r->send_http_header(), не добавлялась кодировка, указанная в директиве charset.\n</para>\n<para lang=\"en\">\na charset set by the \"charset\" directive was not appended\nto the \"Content-Type\" header set by $r->send_http_header().\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании метода /dev/poll\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif /dev/poll method was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.9\" date=\"2007-08-28\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрабочий процесс мог зациклиться при использовании протокола HTTPS;\nошибка появилась в 0.6.7.\n</para>\n<para lang=\"en\">\na worker process may got caught in an endless loop,\nif the HTTPS protocol was used;\nthe bug had appeared in 0.6.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли сервер слушал на двух адресах или портах, то nginx не запускался\nпри использовании wildcard в конце имени сервера.\n</para>\n<para lang=\"en\">\nif server listened on two addresses or ports and trailing wildcard was used,\nthen nginx did not run.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива ip_hash могла неверно помечать сервера как нерабочие.\n</para>\n<para lang=\"en\">\nthe \"ip_hash\" directive might incorrectly mark servers as down.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на amd64;\nошибка появилась в 0.6.8.\n</para>\n<para lang=\"en\">\nnginx could not be built on amd64;\nthe bug had appeared in 0.6.8.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.8\" date=\"2007-08-20\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx пытается установить директивы worker_priority,\nworker_rlimit_nofile, worker_rlimit_core, worker_rlimit_sigpending\nбез привилегий root'а.\n</para>\n<para lang=\"en\">\nnow nginx tries to set the \"worker_priority\", \"worker_rlimit_nofile\",\n\"worker_rlimit_core\", and \"worker_rlimit_sigpending\" without super-user\nprivileges.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx экранирует символы пробела и \"%\" при передаче запроса\nсерверу аутентификации почтового прокси-сервера.\n</para>\n<para lang=\"en\">\nnow nginx escapes space and \"%\" in request to a mail proxy authentication\nserver.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx экранирует символ \"%\" в переменной $memcached_key.\n</para>\n<para lang=\"en\">\nnow nginx escapes \"%\" in $memcached_key variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри указании относительного пути к конфигурационному файлу в качестве\nпараметра ключа -c nginx определял путь относительно конфигурационного префикса;\nошибка появилась в 0.6.6.\n</para>\n<para lang=\"en\">\nnginx used path relative to configuration prefix for non-absolute\nconfiguration file path specified in the \"-c\" key;\nthe bug had appeared in 0.6.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не работал на FreeBSD/sparc64.\n</para>\n<para lang=\"en\">\nnginx did not work on FreeBSD/sparc64.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.7\" date=\"2007-08-15\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь пути, указанные в директивах include, auth_basic_user_file,\nperl_modules, ssl_certificate, ssl_certificate_key и\nssl_client_certificate, определяются относительно каталога конфигурационного\nфайла nginx.conf, а не относительно префикса.\n</para>\n<para lang=\"en\">\nnow the paths specified in the \"include\", \"auth_basic_user_file\",\n\"perl_modules\", \"ssl_certificate\", \"ssl_certificate_key\", and\n\"ssl_client_certificate\" directives are relative to directory of\nnginx configuration file nginx.conf, but not to nginx prefix directory.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр --sysconfdir=PATH в configure упразднён.\n</para>\n<para lang=\"en\">\nthe --sysconfdir=PATH option in configure was canceled.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдля обновления на лету версий 0.1.x создан специальный сценарий\n<nobr>make upgrade1.</nobr>\n</para>\n<para lang=\"en\">\nthe special make target \"upgrade1\" was defined for online upgrade of\n0.1.x versions.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы server_name и valid_referers поддерживают регулярные выражения.\n</para>\n<para lang=\"en\">\nthe \"server_name\" and \"valid_referers\" directives support regular expressions.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server в блоке upstream поддерживает параметр backup.\n</para>\n<para lang=\"en\">\nthe \"server\" directive in the \"upstream\" context supports\nthe \"backup\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает метод $r->discard_request_body.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->discard_request_body.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива \"add_header Last-Modified ...\" меняет строку \"Last-Modified\"\nв заголовке ответа.\n</para>\n<para lang=\"en\">\nthe \"add_header Last-Modified ...\" directive changes the \"Last-Modified\"\nresponse header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли на запрос с телом возвращался ответ с кодом HTTP отличным от 200,\nи после этого запроса соединение переходило в состояние keep-alive,\nто на следующий запрос nginx возвращал 400.\n</para>\n<para lang=\"en\">\nif a response different than 200 was returned to a request with body\nand connection went to the keep-alive state after the request, then\nnginx returned 400 for the next request.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве auth_http был задан неправильный адрес, то\nв рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process\nif invalid address was set in the \"auth_http\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь по умолчанию nginx использует значение 511 для listen backlog\nна всех платформах, кроме FreeBSD.<br/>\nСпасибо Jiang Hong.\n</para>\n<para lang=\"en\">\nnow nginx uses default listen backlog value 511 on all platforms\nexcept FreeBSD.<br/>\nThanks to Jiang Hong.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрабочий процесс мог зациклиться, если server в блоке upstream был помечен\nкак down;\nошибка появилась в 0.6.6.\n</para>\n<para lang=\"en\">\na worker process may got caught in an endless loop, if a \"server\" inside\n\"upstream\" block was marked as \"down\";\nthe bug had appeared in 0.6.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nsendfilev() в Solaris теперь не используется при передаче тела запроса\nFastCGI-серверу через unix domain сокет.\n</para>\n<para lang=\"en\">\nnow Solaris sendfilev() is not used to transfer the client request body\nto FastCGI-server via the unix domain socket.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.6\" date=\"2007-07-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр --sysconfdir=PATH в configure.\n</para>\n<para lang=\"en\">\nthe --sysconfdir=PATH option in configure.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nименованные location'ы.\n</para>\n<para lang=\"en\">\nnamed locations.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременную $args можно устанавливать с помощью set.\n</para>\n<para lang=\"en\">\nthe $args variable can be set with the \"set\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $is_args.\n</para>\n<para lang=\"en\">\nthe $is_args variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nравномерное распределение запросов к апстримам с большими весами.\n</para>\n<para lang=\"en\">\nfair big weight upstream balancer.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли клиент в почтовом прокси-сервере закрывал соединение,\nто nginx мог не закрывать соединение с бэкендом.\n</para>\n<para lang=\"en\">\nif a client has closed connection to mail proxy\n then nginx might not close connection to backend.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании одного хоста в качестве бэкендов для протоколов HTTP и HTTPS\nбез явного указания портов, nginx использовал только один порт&mdash;80 или 443.\n</para>\n<para lang=\"en\">\nif the same host without specified port was used as backend for HTTP and HTTPS,\nthen nginx used only one port&mdash;80 or 443.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Solaris/amd64 Sun Studio 11 и более ранними версиями;\nошибка появилась в 0.6.4.\n</para>\n<para lang=\"en\">\nfix building on Solaris/amd64 by Sun Studio 11 and early versions;\nthe bug had appeared in 0.6.4.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.5\" date=\"2007-07-23\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $nginx_version.<br/>\nСпасибо Николаю Гречуху.\n</para>\n<para lang=\"en\">\n$nginx_version variable.<br/>\nThanks to Nick S. Grechukh.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпочтовый прокси-сервер поддерживает AUTHENTICATE в режиме IMAP.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe mail proxy supports AUTHENTICATE in IMAP mode.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпочтовый прокси-сервер поддерживает STARTTLS в режиме SMTP.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe mail proxy supports STARTTLS in SMTP mode.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx экранирует пробел в переменной $memcached_key.\n</para>\n<para lang=\"en\">\nnow nginx escapes space in $memcached_key variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx неправильно собирался Sun Studio на Solaris/amd64.<br/>\nСпасибо Jiang Hong.\n</para>\n<para lang=\"en\">\nnginx was incorrectly built by Sun Studio on Solaris/amd64.<br/>\nThanks to Jiang Hong.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнезначительных потенциальных ошибок.<br/>\nСпасибо Coverity's Scan.\n</para>\n<para lang=\"en\">\nof minor potential bugs.<br/>\nThanks to Coverity's Scan.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.4\" date=\"2007-07-17\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nпри использовании директивы msie_refresh был возможен XSS.<br/>\nСпасибо Максиму Богуку.\n</para>\n<para lang=\"en\">\nthe \"msie_refresh\" directive allowed XSS.<br/>\nThanks to Maxim Boguk.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы proxy_store и fastcgi_store изменены.\n</para>\n<para lang=\"en\">\nthe \"proxy_store\" and \"fastcgi_store\" directives were changed.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_store_access и fastcgi_store_access.\n</para>\n<para lang=\"en\">\nthe \"proxy_store_access\" and \"fastcgi_store_access\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не работал на Solaris/sparc64, если был собран Sun Studio.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nnginx did not work on Solaris/sparc64 if it was built by Sun Studio.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nобход ошибки в Sun Studio 12.<br/>\nСпасибо Jiang Hong.\n</para>\n<para lang=\"en\">\nfor Sun Studio 12.<br/>\nThanks to Jiang Hong.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.3\" date=\"2007-07-12\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_store и fastcgi_store.\n</para>\n<para lang=\"en\">\nthe \"proxy_store\" and \"fastcgi_store\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы auth_http_header\nв рабочем процессе мог произойти segmentation fault.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif the \"auth_http_header\" directive was used.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовался метод аутентификации CRAM-MD5, но он не был разрешён,\nто в рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process\nif the CRAM-MD5 authentication method was used, but it was not enabled.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании протокола HTTPS в директиве proxy_pass\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process when\nthe HTTPS protocol was used in the \"proxy_pass\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв рабочем процессе мог произойти segmentation fault,\nесли использовался метод eventport.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif the eventport method was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_ignore_client_abort и fastcgi_ignore_client_abort не работали;\nошибка появилась в 0.5.13.\n</para>\n<para lang=\"en\">\nthe \"proxy_ignore_client_abort\" and \"fastcgi_ignore_client_abort\" directives\ndid not work;\nthe bug had appeared in 0.5.13.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.2\" date=\"2007-07-09\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли заголовок ответа был разделён в FastCGI-записях, то nginx передавал\nклиенту мусор в таких заголовках.\n</para>\n<para lang=\"en\">\nif the FastCGI header was split in records,\nthen nginx passed garbage in the header to a client.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.1\" date=\"2007-06-17\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв парсинге SSI.\n</para>\n<para lang=\"en\">\nin SSI parsing.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании удалённого подзапроса в SSI последующий\nподзапрос локального файла мог отдаваться клиенту в неверном порядке.\n</para>\n<para lang=\"en\">\nif remote SSI subrequest was used, then posterior local file subrequest\nmight transferred to client in wrong order.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nбольшие включения в SSI, сохранённые во временные файлы,\nпередавались не полностью.\n</para>\n<para lang=\"en\">\nlarge SSI inclusions buffered in temporary files were truncated.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзначение perl'овой переменной $$ модуля ngx_http_perl_module было равно\nномеру главного процесса.\n</para>\n<para lang=\"en\">\nthe perl $$ variable value in ngx_http_perl_module was equal to the master\nprocess identification number.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.6.0\" date=\"2007-06-14\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы \"server_name\", \"map\", and \"valid_referers\" поддерживают\nмаски вида \"www.example.*\".\n</para>\n<para lang=\"en\">\nthe \"server_name\", \"map\", and \"valid_referers\" directives support\nthe \"www.example.*\" wildcards.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.25\" date=\"2007-06-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-http_rewrite_module;\nошибка появилась в 0.5.24.\n</para>\n<para lang=\"en\">\nnginx could not be built with the --without-http_rewrite_module parameter;\nthe bug had appeared in 0.5.24.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.24\" date=\"2007-06-06\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nдиректива ssl_verify_client не работала, если запрос выполнялся\nпо протоколу HTTP/0.9.\n</para>\n<para lang=\"en\">\nthe \"ssl_verify_client\" directive did not work if request was made\nusing HTTP/0.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании сжатия часть ответа могла передаваться несжатой;\nошибка появилась в 0.5.23.\n</para>\n<para lang=\"en\">\na part of response body might be passed uncompressed if gzip was used;\nthe bug had appeared in 0.5.23.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.23\" date=\"2007-06-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssl_module поддерживает расширение TLS Server Name Indication.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssl_module supports Server Name Indication TLS extension.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_catch_stderr.<br/>\nСпасибо Николаю Гречуху, проект OWOX.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_catch_stderr\" directive.<br/>\nThanks to Nick S. Grechukh, OWOX project.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна Линуксе в основном процессе происходил segmentation fault,\nесли два виртуальных сервера должны bind()ится к пересекающимся портам.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in master process if\ntwo virtual servers should bind() to the overlapping ports.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx был собран с модулем ngx_http_perl_module и perl\nподдерживал потоки, то во время второй переконфигурации\nвыдавались ошибки \"panic: MUTEX_LOCK\" и \"perl_parse() failed\".\n</para>\n<para lang=\"en\">\nif nginx was built with ngx_http_perl_module and perl supported threads,\nthen during second reconfiguration the error messages\n\"panic: MUTEX_LOCK\" and \"perl_parse() failed\" were issued.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв использовании протокола HTTPS в директиве proxy_pass.\n</para>\n<para lang=\"en\">\nin the HTTPS protocol in the \"proxy_pass\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.22\" date=\"2007-05-29\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nбольшое тело запроса могло не передаваться бэкенду;\nошибка появилась в 0.5.21.\n</para>\n<para lang=\"en\">\na big request body might not be passed to backend;\nthe bug had appeared in 0.5.21.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.21\" date=\"2007-05-28\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли внутри сервера описано больше примерно десяти location'ов,\nто location'ы, заданные с помощью регулярного выражения,\nмогли выполняться не в том, порядке, в каком они описаны.\n</para>\n<para lang=\"en\">\nif server has more than about ten locations, then regex locations\nmight be chosen not in that order as they were specified.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна 64-битной платформе рабочий процесс мог зациклиться, если 33-тий\nпо счёту или последующий бэкенд упал.<br/>\nСпасибо Антону Поварову.\n</para>\n<para lang=\"en\">\na worker process may got caught in an endless loop on 64-bit platform,\nif the 33-rd or next in succession backend has failed.<br/>\nThanks to Anton Povarov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании библиотеки PCRE на Solaris/sparc64\nмог произойти bus error.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\na bus error might occur on Solaris/sparc64 if the PCRE library was used.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв использовании протокола HTTPS в директиве proxy_pass.\n</para>\n<para lang=\"en\">\nin the HTTPS protocol in the \"proxy_pass\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.20\" date=\"2007-05-07\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива sendfile_max_chunk.\n</para>\n<para lang=\"en\">\nthe \"sendfile_max_chunk\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные \"$http_...\", \"$sent_http_...\" и \"$upstream_http_...\"\nможно менять директивой set.\n</para>\n<para lang=\"en\">\nthe \"$http_...\", \"$sent_http_...\", and \"$upstream_http_...\" variables\nmay be changed using the \"set\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SSI-команды 'if expr=\"$var = /\"'\nв рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif the SSI command 'if expr=\"$var = /\"' was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзавершающая строка multipart range ответа передавалась неверно.<br/>\nСпасибо Evan Miller.\n</para>\n<para lang=\"en\">\ntrailing boundary of multipart range response was transferred incorrectly.<br/>\nThanks to Evan Miller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не работал на Solaris/sparc64, если был собран Sun Studio.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nnginx did not work on Solaris/sparc64 if it was built by Sun Studio.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module не собирался make в Solaris.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module could not be built by Solaris make.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.19\" date=\"2007-04-24\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nзначение переменной $request_time теперь записывается с точностью\nдо миллисекунд.\n</para>\n<para lang=\"en\">\nnow the $request_time variable has millisecond precision.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nметод $r->rflush в модуле ngx_http_perl_module переименован в $r->flush.\n</para>\n<para lang=\"en\">\nthe method $r->rflush of ngx_http_perl_module was renamed to the $r->flush.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $upstream_addr.\n</para>\n<para lang=\"en\">\nthe $upstream_addr variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_headers_hash_max_size и proxy_headers_hash_bucket_size.<br/>\nСпасибо Володымыру Костырко.\n</para>\n<para lang=\"en\">\nthe \"proxy_headers_hash_max_size\" and \"proxy_headers_hash_bucket_size\"\ndirectives.<br/>\nThanks to Volodymyr Kostyrko.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании sendfile и limit_rate на 64-битных платформах\nнельзя было передавать файлы больше 2G.\n</para>\n<para lang=\"en\">\nthe files more than 2G could not be transferred using sendfile and limit_rate\non 64-bit platforms.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании sendfile на 64-битном Linux нельзя было передавать файлы\nбольше 2G.\n</para>\n<para lang=\"en\">\nthe files more than 2G could not be transferred using sendfile on 64-bit Linux.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.18\" date=\"2007-04-19\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_sub_filter_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_sub_filter_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные \"$upstream_http_...\".\n</para>\n<para lang=\"en\">\nthe \"$upstream_http_...\" variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь переменные $upstream_status и $upstream_response_time\nсодержат данные о всех обращениях к апстримам, сделанным до X-Accel-Redirect.\n</para>\n<para lang=\"en\">\nnow the $upstream_status and $upstream_response_time variables\nkeep data about all upstreams before X-Accel-Redirect.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx был собран с модулем ngx_http_perl_module и perl\nне поддерживал multiplicity, то после первой переконфигурации\nи после получения любого сигнала\nв основном процессе происходил segmentation fault;\nошибка появилась в 0.5.9.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in master process\nafter first reconfiguration and receiving any signal\nif nginx was built with ngx_http_perl_module and perl\ndid not support multiplicity;\nthe bug had appeared in 0.5.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли perl не поддерживал multiplicity, то после переконфигурации\nперловый код не работал;\nошибка появилась в 0.3.38.\n</para>\n<para lang=\"en\">\nif perl did not support multiplicity, then after reconfiguration\nperl code did not work;\nthe bug had appeared in 0.3.38.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.17\" date=\"2007-04-02\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx для метода TRACE всегда возвращает код 405.\n</para>\n<para lang=\"en\">\nnow nginx always returns the 405 status for the TRACE method.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx поддерживает директиву include внутри блока types.\n</para>\n<para lang=\"en\">\nnow nginx supports the \"include\" directive inside the \"types\" block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nиспользование переменной $document_root в директиве root и alias\nзапрещено: оно вызывало рекурсивное переполнение стека.\n</para>\n<para lang=\"en\">\nthe $document_root variable usage in the \"root\" and \"alias\" directives\nis disabled: this caused recursive stack overflow.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв использовании протокола HTTPS в директиве proxy_pass.\n</para>\n<para lang=\"en\">\nin the HTTPS protocol in the \"proxy_pass\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв некоторых случаях некэшируемые переменные (такие, как $uri)\nвозвращали старое закэшированное значение.\n</para>\n<para lang=\"en\">\nin some cases non-cacheable variables (such as $uri variable)\nreturned old cached value.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.16\" date=\"2007-03-26\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв качестве ключа для хэша в директиве ip_hash не использовалась сеть\nкласса С.<br/>\nСпасибо Павлу Ярковому.\n</para>\n<para lang=\"en\">\nthe C-class network was not used as hash key in the \"ip_hash\" directive.<br/>\nThanks to Pavel Yarkovoy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в строке \"Content-Type\" в заголовке ответа бэкенда был указан charset\nи строка завершалась символом \";\",\nто в рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.3.50.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif a charset was set in the \"Content-Type\" header line and the line\nhas trailing \";\";\nthe bug had appeared in 0.3.50.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"[alert] zero size buf\" при работе с FastCGI-сервером, если\nтело запроса, записанное во временный файл, было кратно 32K.\n</para>\n<para lang=\"en\">\nthe \"[alert] zero size buf\" error when FastCGI server was used and\na request body written in a temporary file was multiple of 32K.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Solaris без параметра --with-debug;\nошибка появилась в 0.5.15.\n</para>\n<para lang=\"en\">\nnginx could not be built on Solaris without the --with-debug option;\nthe bug had appeared in 0.5.15.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.15\" date=\"2007-03-19\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпочтовый прокси-сервер поддерживает аутентифицированное SMTP-проксирование и\nдирективы smtp_auth, smtp_capabilities и xclient.<br/>\nСпасибо Антону Южанинову и Максиму Дунину.\n</para>\n<para lang=\"en\">\nthe mail proxy supports authenticated SMTP proxying and\nthe \"smtp_auth\", \"smtp_capabilities\", and \"xclient\" directives.<br/>\nThanks to Anton Yuzhaninov and Maxim Dounin.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь keep-alive соединения закрываются сразу же по получении сигнала\nпереконфигурации.\n</para>\n<para lang=\"en\">\nnow the keep-alive connections are closed just after receiving\nthe reconfiguration signal.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы imap и auth переименованы соответственно в mail и pop3_auth.\n</para>\n<para lang=\"en\">\nthe \"imap\" and \"auth\" directives were renamed\nto the \"mail\" and \"pop3_auth\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовался метод аутентификации CRAM-MD5 и не был разрешён метод APOP,\nто в рабочем процессе происходил segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault occurred in worker process\nif the CRAM-MD5 authentication method was used\nand the APOP method was disabled.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы starttls only в протоколе POP3 nginx\nразрешал аутентификацию без перехода в режим SSL.\n</para>\n<para lang=\"en\">\nif the \"starttls only\" directive was used in POP3 protocol,\nthen nginx allowed authentication without switching to the SSL mode.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрабочие процессы не выходили после переконфигурации и не переоткрывали логи,\nесли использовался метод eventport.\n</para>\n<para lang=\"en\">\nworker processes did not exit after reconfiguration and\ndid not rotate logs if the eventport method was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы ip_hash рабочий процесс мог зациклиться.\n</para>\n<para lang=\"en\">\na worker process may got caught in an endless loop,\nif the \"ip_hash\" directive was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь nginx не пишет в лог некоторые alert'ы,\nесли используются методы eventport или /dev/poll.\n</para>\n<para lang=\"en\">\nnow nginx does not log some alerts if eventport or /dev/poll methods are used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.14\" date=\"2007-02-23\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx игнорировал лишние закрывающие скобки \"}\" в конце\nконфигурационного файла.\n</para>\n<para lang=\"en\">\nnginx ignored superfluous closing \"}\" in the end of configuration file.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.13\" date=\"2007-02-19\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nметоды COPY и MOVE.\n</para>\n<para lang=\"en\">\nthe COPY and MOVE methods.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_realip_module устанавливал мусор для запросов,\nпереданных по keep-alive соединению.\n</para>\n<para lang=\"en\">\nthe ngx_http_realip_module set garbage for requests passed via\nkeep-alive connection.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не работал на 64-битном big-endian Linux.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nnginx did not work on big-endian 64-bit Linux.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри получении слишком длинной команды IMAP/POP3-прокси теперь сразу\nзакрывает соединение, а не по таймауту.\n</para>\n<para lang=\"en\">\nnow when IMAP/POP3 proxy receives too long command it closes the connection\nright away, but not after timeout.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли при использовании метода epoll клиент закрывал преждевременно\nсоединение со своей стороны, то nginx закрывал это соединение только\nпо истечении таймаута на передачу.\n</para>\n<para lang=\"en\">\nif the \"epoll\" method was used and a client closed a connection prematurely,\nthen nginx closed the connection after a send timeout only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на платформах, отличных от i386, amd64, sparc и ppc;\nошибка появилась в 0.5.8.\n</para>\n<para lang=\"en\">\nnginx could not be built on platforms different from i386, amd64, sparc,\nand ppc;\nthe bug had appeared in 0.5.8.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.12\" date=\"2007-02-12\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на платформах, отличных от i386, amd64, sparc и ppc;\nошибка появилась в 0.5.8.\n</para>\n<para lang=\"en\">\nnginx could not be built on platforms different from i386, amd64, sparc,\nand ppc;\nthe bug had appeared in 0.5.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании временных файлов в время работы с FastCGI-сервером\nв рабочем процессе мог произойти segmentation fault;\nошибка появилась в 0.5.8.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif the temporary files were used while working with FastCGI server;\nthe bug had appeared in 0.5.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли переменная $fastcgi_script_name записывалась в лог,\nто в рабочем процессе мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif the $fastcgi_script_name variable was logged.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nngx_http_perl_module не собирался на Solaris.\n</para>\n<para lang=\"en\">\nngx_http_perl_module could not be built on Solaris.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.11\" date=\"2007-02-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь configure определяет библиотеку PCRE в MacPorts.<br/>\nСпасибо Chris McGrath.\n</para>\n<para lang=\"en\">\nnow configure detects system PCRE library in MacPorts.<br/>\nThanks to Chris McGrath.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответ был неверным, если запрашивалось несколько диапазонов;\nошибка появилась в 0.5.6.\n</para>\n<para lang=\"en\">\nthe response was incorrect if several ranges were requested;\nthe bug had appeared in 0.5.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива create_full_put_path не могла создавать промежуточные каталоги,\nесли не была установлена директива dav_access.<br/>\nСпасибо Evan Miller.\n</para>\n<para lang=\"en\">\nthe \"create_full_put_path\" directive could not create the intermediate\ndirectories if no \"dav_access\" directive was set.<br/>\nThanks to Evan Miller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nвместо кодов ошибок \"400\" и \"408\" в access_log мог записываться код \"0\".\n</para>\n<para lang=\"en\">\nthe \"0\" response code might be logged in the access_log instead of\nthe \"400\" and \"408\" error codes.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри сборке с оптимизацией -O2 в рабочем процессе мог произойти\nsegmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif nginx was built with -O2 optimization.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.10\" date=\"2007-01-26\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво время обновления исполняемого файла новый процесс не наследовал\nслушающие сокеты;\nошибка появилась в 0.5.9.\n</para>\n<para lang=\"en\">\nwhile online executable file upgrade the new master process did not\ninherit the listening sockets;\nthe bug had appeared in 0.5.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри сборке с оптимизацией -O2 в рабочем процессе мог произойти\nsegmentation fault;\nошибка появилась в 0.5.1.\n</para>\n<para lang=\"en\">\na segmentation fault might occur in worker process\nif nginx was built with -O2 optimization;\nthe bug had appeared in 0.5.1.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.9\" date=\"2007-01-25\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nмодуль ngx_http_memcached_module теперь в качестве ключа использует\nзначение переменной $memcached_key.\n</para>\n<para lang=\"en\">\nnow the ngx_http_memcached_module uses the $memcached_key variable value\nas a key.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $memcached_key.\n</para>\n<para lang=\"en\">\nthe $memcached_key variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр clean в директиве client_body_in_file_only.\n</para>\n<para lang=\"en\">\nthe \"clean\" parameter in the \"client_body_in_file_only\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива env.\n</para>\n<para lang=\"en\">\nthe \"env\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива sendfile работает внутри блока if.\n</para>\n<para lang=\"en\">\nthe \"sendfile\" directive is available inside the \"if\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь при ошибке записи в access_log nginx записывает сообщение в error_log,\nно не чаще одного раза в минуту.\n</para>\n<para lang=\"en\">\nnow on failure of the writing to access nginx logs a message to error_log,\nbut not more often than once a minute.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива \"access_log off\" не всегда запрещала запись в лог.\n</para>\n<para lang=\"en\">\nthe \"access_log off\" directive did not always turn off the logging.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.8\" date=\"2007-01-19\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли использовалась директива <nobr>\"client_body_in_file_only on\"</nobr>\nи тело запроса было небольшое, то мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur if\n<nobr>\"client_body_in_file_only on\"</nobr> was used\nand a request body was small.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроисходил segmentation fault, если использовались директивы\n<nobr>\"client_body_in_file_only on\"</nobr>\nи <nobr>\"proxy_pass_request_body off\"</nobr>\nили <nobr>\"fastcgi_pass_request_body off\"</nobr>,\nи делался переход к следующему бэкенду.\n</para>\n<para lang=\"en\">\na segmentation fault occurred if <nobr>\"client_body_in_file_only on\"</nobr>\nand <nobr>\"proxy_pass_request_body off\"</nobr>\nor <nobr>\"fastcgi_pass_request_body off\"</nobr>\ndirectives were used, and nginx switched to a next upstream.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли при использовании директивы \"proxy_buffering off\" соединение с клиентом\nбыло неактивно, то оно закрывалось по таймауту, заданному директивой\nsend_timeout;\nошибка появилась в 0.4.7.\n</para>\n<para lang=\"en\">\nif the \"proxy_buffering off\" directive was used and a client connection\nwas non-active, then the connection was closed after send timeout;\nthe bug had appeared in 0.4.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли при использовании метода epoll клиент закрывал преждевременно\nсоединение со своей стороны, то nginx закрывал это соединение только\nпо истечении таймаута на передачу.\n</para>\n<para lang=\"en\">\nif the \"epoll\" method was used and a client closed a connection prematurely,\nthen nginx closed the connection after a send timeout only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"[alert] zero size buf\" при работе с FastCGI-сервером.\n</para>\n<para lang=\"en\">\nthe \"[alert] zero size buf\" error when FastCGI server was used.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nИсправление ошибок в директиве limit_zone.\n</para>\n<para lang=\"en\">\nBugfixes in the \"limit_zone\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.7\" date=\"2007-01-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nоптимизация использования памяти в ssl_session_cache.\n</para>\n<para lang=\"en\">\nthe ssl_session_cache storage optimization.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nИсправление ошибок в директивах ssl_session_cache и limit_zone.\n</para>\n<para lang=\"en\">\nBugfixes in the \"ssl_session_cache\" and \"limit_zone\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна старте или во время переконфигурации происходил segmentation fault,\nесли директивы ssl_session_cache или limit_zone использовались\nна 64-битных платформах.\n</para>\n<para lang=\"en\">\nthe segmentation fault was occurred on start or while reconfiguration\nif the \"ssl_session_cache\" or \"limit_zone\" directives were used\non 64-bit platforms.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директив add_before_body или add_after_body происходил\nsegmentation fault, если в заголовке ответа нет строки \"Content-Type\".\n</para>\n<para lang=\"en\">\na segmentation fault occurred if the \"add_before_body\" or \"add_after_body\"\ndirectives were used and there was no \"Content-Type\" header line in response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nбиблиотека OpenSSL всегда собиралась с поддержкой потоков.<br/>\nСпасибо Дену Иванову.\n</para>\n<para lang=\"en\">\nthe OpenSSL library was always built with the threads support.<br/>\nThanks to Den Ivanov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость библиотеки PCRE-6.5+ и компилятора icc.\n</para>\n<para lang=\"en\">\nthe PCRE-6.5+ library and the icc compiler compatibility.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.6\" date=\"2007-01-09\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь модуль ngx_http_index_module игнорирует все методы,\nкроме GET, HEAD и POST.\n</para>\n<para lang=\"en\">\nnow the ngx_http_index_module ignores all methods except the GET, HEAD, and\nPOST methods.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_limit_zone_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_limit_zone_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $binary_remote_addr.\n</para>\n<para lang=\"en\">\nthe $binary_remote_addr variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssl_session_cache модулей ngx_http_ssl_module и ngx_imap_ssl_module.\n</para>\n<para lang=\"en\">\nthe \"ssl_session_cache\" directives\nof the ngx_http_ssl_module and ngx_imap_ssl_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nметод DELETE поддерживает рекурсивное удаление.\n</para>\n<para lang=\"en\">\nthe DELETE method supports recursive removal.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании $r->sendfile() byte-ranges передавались неверно.\n</para>\n<para lang=\"en\">\nthe byte-ranges were transferred incorrectly if the $r->sendfile() was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.5\" date=\"2006-12-24\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nключ -v больше не выводит информацию о компиляторе.\n</para>\n<para lang=\"en\">\nthe -v switch does not show compiler information any more.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -V.\n</para>\n<para lang=\"en\">\nthe -V switch.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_rlimit_core поддерживает указание размера в K, M и G.\n</para>\n<para lang=\"en\">\nthe \"worker_rlimit_core\" directive supports size in K, M, and G.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль nginx.pm теперь может устанавливаться непривилегированным пользователем.\n</para>\n<para lang=\"en\">\nthe nginx.pm module now could be installed by an unprivileged user.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании методов $r->request_body или $r->request_body_file мог\nпроизойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur if the $r->request_body or\n$r->request_body_file methods were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибок, специфичных для платформы ppc.\n</para>\n<para lang=\"en\">\nthe ppc platform specific bugs.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.4\" date=\"2006-12-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу perl можно использовать внутри блока limit_except.\n</para>\n<para lang=\"en\">\nthe \"perl\" directive may be used inside the \"limit_except\" block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module требовал строку \"Date\" в заголовке запроса\nдля метода DELETE.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module required the \"Date\" request header line\nfor the DELETE method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании одного параметра в директиве dav_access nginx мог\nсообщить об ошибке в конфигурации.\n</para>\n<para lang=\"en\">\nif one only parameter was used in the \"dav_access\" directive, then\nnginx might report about configuration error.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменной $host мог произойти segmentation fault;\nошибка появилась в 0.4.14.\n</para>\n<para lang=\"en\">\na segmentation fault might occur if the $host variable was used;\nthe bug had appeared in 0.4.14.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.3\" date=\"2006-12-13\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает методы $r->status, $r->log_error\nи $r->sleep.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->status, $r->log_error,\nand $r->sleep methods.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nметод $r->variable поддерживает переменные, неописанные в конфигурации nginx'а.\n</para>\n<para lang=\"en\">\nthe $r->variable method supports variables that do not exist in nginx\nconfiguration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод $r->has_request_body не работал.\n</para>\n<para lang=\"en\">\nthe $r->has_request_body method did not work.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.2\" date=\"2006-12-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директивах proxy_pass использовалось имя, указанное в upstream,\nто nginx пытался найти IP-адрес этого имени;\nошибка появилась в 0.5.1.\n</para>\n<para lang=\"en\">\nif the \"proxy_pass\" directive used the name of the \"upstream\" block,\nthen nginx tried to resolve the name;\nthe bug had appeared in 0.5.1.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.1\" date=\"2006-12-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива post_action могла не работать после неудачного завершения запроса.\n</para>\n<para lang=\"en\">\nthe \"post_action\" directive might not run after a unsuccessful completion\nof a request.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nобход ошибки в Eudora для Mac;\nошибка появилась в 0.4.11.<br/>\nСпасибо Bron Gondwana.\n</para>\n<para lang=\"en\">\nfor Eudora for Mac;\nthe bug had appeared in 0.4.11.<br/>\nThanks to Bron Gondwana.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри указании в директиве fastcgi_pass имени описанного upstream'а выдавалось\nсообщение \"no port in upstream\";\nошибка появилась в 0.5.0.\n</para>\n<para lang=\"en\">\nif the \"upstream\" name was used in the \"fastcgi_pass\", then the message\n\"no port in upstream\" was issued;\nthe bug had appeared in 0.5.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директивах proxy_pass и fastcgi_pass использовались одинаковых имена\nсерверов, но с разными портами, то эти директивы использовали первый\nописанный порт;\nошибка появилась в 0.5.0.\n</para>\n<para lang=\"en\">\nif the \"proxy_pass\" and \"fastcgi_pass\" directives used the same servers but\ndifferent ports, then these directives uses the first described port;\nthe bug had appeared in 0.5.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директивах proxy_pass и fastcgi_pass использовались unix domain сокеты,\nто эти директивы использовали первый описанный сокет;\nошибка появилась в 0.5.0.\n</para>\n<para lang=\"en\">\nif the \"proxy_pass\" and \"fastcgi_pass\" directives used the unix domain sockets,\nthen these directives used first described socket;\nthe bug had appeared in 0.5.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nngx_http_auth_basic_module игнорировал пользователя, если он был указан\nв последней строке файла паролей и после пароля не было перевода строки,\nвозврата каретки или символа \":\".\n</para>\n<para lang=\"en\">\nngx_http_auth_basic_module ignored the user if it was in the last line in\nthe password file and there was no the carriage return, the line feed,\nor the \":\" symbol after the password.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $upstream_response_time могла быть равна \"0.000\", хотя время\nобработки было больше 1 миллисекунды.\n</para>\n<para lang=\"en\">\nthe $upstream_response_time variable might be equal to \"0.000\", although\nresponse time was more than 1 millisecond.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.5.0\" date=\"2006-12-04\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметры в виде \"%name\" в директиве log_format больше не поддерживаются.\n</para>\n<para lang=\"en\">\nthe parameters in the \"%name\" form in the \"log_format\" directive\nare not supported anymore.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы proxy_upstream_max_fails, proxy_upstream_fail_timeout,\nfastcgi_upstream_max_fails, и fastcgi_upstream_fail_timeout,\nmemcached_upstream_max_fails и memcached_upstream_fail_timeout\nбольше не поддерживаются.\n</para>\n<para lang=\"en\">\nthe \"proxy_upstream_max_fails\", \"proxy_upstream_fail_timeout\",\n\"fastcgi_upstream_max_fails\", \"fastcgi_upstream_fail_timeout\",\n\"memcached_upstream_max_fails\", and \"memcached_upstream_fail_timeout\"\ndirectives are not supported anymore.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server в блоке upstream поддерживает параметры\nmax_fails, fail_timeout и down.\n</para>\n<para lang=\"en\">\nthe \"server\" directive in the \"upstream\" context supports\nthe \"max_fails\", \"fail_timeout\", and \"down\" parameters.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ip_hash в блоке upstream.\n</para>\n<para lang=\"en\">\nthe \"ip_hash\" directive inside the \"upstream\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nстатус WAIT в строке \"Auth-Status\" в заголовке ответа сервера аутентификации\nIMAP/POP3 прокси.\n</para>\n<para lang=\"en\">\nthe WAIT status in the \"Auth-Status\" header line of the IMAP/POP3 proxy\nauthentication server response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на 64-битных платформах;\nошибка появилась в 0.4.14.\n</para>\n<para lang=\"en\">\nnginx could not be built on 64-bit platforms;\nthe bug had appeared in 0.4.14.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.14\" date=\"2006-11-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_pass_error_message в IMAP/POP3 прокси.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass_error_message\" directive in IMAP/POP3 proxy.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь configure определяет библиотеку PCRE на FreeBSD, Linux и NetBSD.\n</para>\n<para lang=\"en\">\nnow configure detects system PCRE library on FreeBSD, Linux, and NetBSD.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nngx_http_perl_module не работал с перлом, собранным с поддержкой потоков;\nошибка появилась в 0.3.38.\n</para>\n<para lang=\"en\">\nngx_http_perl_module did not work with perl built with the threads support;\nthe bug had appeared in 0.3.38.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nngx_http_perl_module не работал корректно, если перл вызывался рекурсивно.\n</para>\n<para lang=\"en\">\nngx_http_perl_module did not work if perl was called recursively.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx игнорировал имя сервера в строке запроса.\n</para>\n<para lang=\"en\">\nnginx ignored a host name in a request line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли FastCGI сервер передавал много в stderr,\nто рабочий процесс мог зациклиться.\n</para>\n<para lang=\"en\">\na worker process may got caught in an endless loop,\nif a FastCGI server sent too many data to the stderr.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри изменении системного времени переменная $upstream_response_time\nмогла быть отрицательной.\n</para>\n<para lang=\"en\">\nthe $upstream_response_time variable may be negative if the system time\nwas changed backward.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании POP3 серверу аутентификации IMAP/POP3 прокси\nне передавался параметр Auth-Login-Attempt.\n</para>\n<para lang=\"en\">\nthe \"Auth-Login-Attempt\" parameter was not sent to\nIMAP/POP3 proxy authentication server when POP3 was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри ошибке соединения с сервером аутентификации IMAP/POP3 прокси\nмог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur if connect to IMAP/POP3 proxy\nauthentication server failed.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.13\" date=\"2006-11-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективу proxy_pass можно использовать внутри блока limit_except.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\" directive may be used inside the \"limit_except\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива limit_except поддерживает все WebDAV методы.\n</para>\n<para lang=\"en\">\nthe \"limit_except\" directive supports all WebDAV methods.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы add_before_body без директивы add_after_body\nответ передавался не полностью.\n</para>\n<para lang=\"en\">\nif the \"add_before_body\" directive was used without\nthe \"add_after_body\" directive, then a response did not transferred complete.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nбольшое тело запроса не принималось, если использовались метод epoll\nи deferred accept().\n</para>\n<para lang=\"en\">\na large request body did not receive if the epoll method\nand the deferred accept() were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдля ответов модуля ngx_http_autoindex_module не выставлялась кодировка;\nошибка появилась в 0.3.50.\n</para>\n<para lang=\"en\">\na charset could not be set for ngx_http_autoindex_module responses;\nthe bug had appeared in 0.3.50.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"[alert] zero size buf\" при работе с FastCGI-сервером;\n</para>\n<para lang=\"en\">\nthe \"[alert] zero size buf\" error when FastCGI server was used;\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр конфигурации --group= игнорировался.<br/>\nСпасибо Thomas Moschny.\n</para>\n<para lang=\"en\">\nthe --group= configuration parameter was ignored.<br/>\nThanks to Thomas Moschny.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\n50-й подзапрос в SSI ответе не работал;\nошибка появилась в 0.3.50.\n</para>\n<para lang=\"en\">\nthe 50th subrequest in SSI response did not work;\nthe bug had appeared in 0.3.50.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.12\" date=\"2006-10-31\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает метод $r->variable.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->variable method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включении в ответ большого статического файла с помощью SSI\nответ мог передаваться не полностью.\n</para>\n<para lang=\"en\">\nif a big static file was included using SSI in a response,\nthen the response may be transferred incomplete.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не убирал \"#fragment\" в URI.\n</para>\n<para lang=\"en\">\nnginx did not omit the \"#fragment\" part in URI.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.11\" date=\"2006-10-25\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nPOP3 прокси поддерживает AUTH LOGIN PLAIN и CRAM-MD5.\n</para>\n<para lang=\"en\">\nthe POP3 proxy supports the AUTH LOGIN PLAIN and CRAM-MD5.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает метод $r->allow_ranges.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->allow_ranges method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включённой поддержке команды APOP в POP3 прокси могли\nне работать команды USER/PASS;\nошибка появилась в 0.4.10.\n</para>\n<para lang=\"en\">\nif the APOP was enabled in the POP3 proxy, then the USER/PASS commands\nmight not work;\nthe bug had appeared in 0.4.10.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.10\" date=\"2006-10-23\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nPOP3 прокси поддерживает APOP.\n</para>\n<para lang=\"en\">\nthe POP3 proxy supports the APOP command.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании методов select, poll и /dev/poll во время ожидания\nответа от сервера аутентификации IMAP/POP3 прокси нагружал процессор.\n</para>\n<para lang=\"en\">\nif the select, poll or /dev/poll methods were used, then while\nwaiting authentication server response the IMAP/POP3 proxy hogged CPU.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменной $server_addr в директиве map мог\nпроизойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault might occur if the $server_addr variable was used\nin the \"map\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_flv_module не поддерживал byte ranges для полных ответов;\nошибка появилась в 0.4.7.\n</para>\n<para lang=\"en\">\nthe ngx_http_flv_module did not support the byte ranges for full responses;\nthe bug had appeared in 0.4.7.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Debian amd64;\nошибка появилась в 0.4.9.\n</para>\n<para lang=\"en\">\nnginx could not be built on Debian amd64;\nthe bug had appeared in 0.4.9.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.9\" date=\"2006-10-13\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр set в команде SSI include.\n</para>\n<para lang=\"en\">\nthe \"set\" parameter in the \"include\" SSI command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module теперь проверяет версию модуля nginx.pm.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module now tests the nginx.pm module version.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.8\" date=\"2006-10-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли до команды SSI include с параметром wait выполнялась ещё\nодна команда SSI include, то параметр wait мог не работать.\n</para>\n<para lang=\"en\">\nif an \"include\" SSI command were before another \"include\" SSI command\nwith a \"wait\" parameter, then the \"wait\" parameter might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_flv_module добавлял FLV-заголовок для полных ответов.<br/>\nСпасибо Алексею Ковырину.\n</para>\n<para lang=\"en\">\nthe ngx_http_flv_module added the FLV header to the full responses.<br/>\nThanks to Alexey Kovyrin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.7\" date=\"2006-10-10\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_flv_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_flv_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $request_body_file.\n</para>\n<para lang=\"en\">\nthe $request_body_file variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы charset и source_charset поддерживают переменные.\n</para>\n<para lang=\"en\">\nthe \"charset\" and \"source_charset\" directives support the variables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли до команды SSI include с параметром wait выполнялась ещё\nодна команда SSI include, то параметр wait мог не работать.\n</para>\n<para lang=\"en\">\nif an \"include\" SSI command were before another \"include\" SSI command\nwith a \"wait\" parameter, then the \"wait\" parameter might not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании директивы \"proxy_buffering off\" или при работе\nс memcached соединения могли не закрываться по таймауту.\n</para>\n<para lang=\"en\">\nif the \"proxy_buffering off\" directive was used or while working with\nmemcached the connections might not be closed on timeout.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не запускался на 64-битных платформах, отличных от amd64, sparc64 и ppc64.\n</para>\n<para lang=\"en\">\nnginx did not run on 64-bit platforms except amd64, sparc64, and ppc64.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.6\" date=\"2006-10-06\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не запускался на 64-битных платформах, отличных от amd64, sparc64 и ppc64.\n</para>\n<para lang=\"en\">\nnginx did not run on 64-bit platforms except amd64, sparc64, and ppc64.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри запросе версии HTTP/1.1 nginx передавал ответ chunk'ами,\nесли длина ответа в методе $r->headers_out(\"Content-Length\", ...)\nбыла задана текстовой строкой.\n</para>\n<para lang=\"en\">\nnginx sent the chunked response for HTTP/1.1 request,<br/>\nif its length was set by text string in\nthe $r->headers_out(\"Content-Length\", ...) method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле перенаправления ошибки с помощью директивы error_page любая директива\nмодуля ngx_http_rewrite_module возвращала эту ошибку;\nошибка появилась в 0.4.4.\n</para>\n<para lang=\"en\">\nafter redirecting error by an \"error_page\" directive\nany ngx_http_rewrite_module directive returned this error code;\nthe bug had appeared in 0.4.4.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.5\" date=\"2006-10-02\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Linux и Solaris;\nошибка появилась в 0.4.4.\n</para>\n<para lang=\"en\">\nnginx could not be built on Linux and Solaris;\nthe bug had appeared in 0.4.4.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.4\" date=\"2006-10-02\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $scheme.\n</para>\n<para lang=\"en\">\nthe $scheme variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива expires поддерживает параметр max.\n</para>\n<para lang=\"en\">\nthe \"expires\" directive supports the \"max\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива include поддерживает маску \"*\".<br/>\nСпасибо Jonathan Dance.\n</para>\n<para lang=\"en\">\nthe \"include\" directive supports the \"*\" mask.<br/>\nThanks to Jonathan Dance.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива return всегда изменяла код ответа, перенаправленного\nдирективой error_page.\n</para>\n<para lang=\"en\">\nthe \"return\" directive always overrode the \"error_page\" response code\nredirected by the \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроисходил segmentation fault, если в методе PUT передавалось\nтело нулевой длины.\n</para>\n<para lang=\"en\">\na segmentation fault occurred if zero-length body was in PUT method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании переменных в директиве proxy_redirect редирект\nизменялся неверно.\n</para>\n<para lang=\"en\">\nthe redirect was changed incorrectly if the variables were used\nin the \"proxy_redirect\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.3\" date=\"2006-09-26\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nошибку 499 теперь нельзя перенаправить с помощью директивы error_page.\n</para>\n<para lang=\"en\">\nnow the 499 error could not be redirected using an \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка Solaris 10 event ports.\n</para>\n<para lang=\"en\">\nthe Solaris 10 event ports support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_browser_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_browser_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении ошибки 400 проксированному серверу\nпомощью директивы error_page мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\na segmentation fault may occur while redirecting the 400 error\nto the proxied server using a \"proxy_pass\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроисходил segmentation fault, если в директиве proxy_pass использовался\nunix domain сокет;\nошибка появилась в 0.3.47.\n</para>\n<para lang=\"en\">\na segmentation fault occurred if an unix domain socket was used in\na \"proxy_pass\" directive;\nthe bug had appeared in 0.3.47.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nSSI не работал с ответами memcached и небуферизированными проксированными\nответами.\n</para>\n<para lang=\"en\">\nSSI did work with memcached and nonbuffered responses.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nобход ошибки PAUSE hardware capability в Sun Studio.\n</para>\n<para lang=\"en\">\nof the Sun Studio PAUSE hardware capability bug.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.2\" date=\"2006-09-14\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nубрана поддержка флага O_NOATIME на Linux;\nошибка появилась в 0.4.1.\n</para>\n<para lang=\"en\">\nthe O_NOATIME flag support on Linux was canceled;\nthe bug had appeared in 0.4.1.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.1\" date=\"2006-09-14\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с DragonFlyBSD.<br/>\nСпасибо Павлу Назарову.\n</para>\n<para lang=\"en\">\nthe DragonFlyBSD compatibility.<br/>\nThanks to Pavel Nazarov.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nобход ошибки в sendfile() в 64-битном Linux при передаче файлов больше 2G.\n</para>\n<para lang=\"en\">\nof bug in 64-bit Linux sendfile(), when file is more than 2G.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь на Linux nginx для статических запросов использует флаг O_NOATIME.<br/>\nСпасибо Yusuf Goolamabbas.\n</para>\n<para lang=\"en\">\nnow on Linux nginx uses O_NOATIME flag for static requests.<br/>\nThanks to Yusuf Goolamabbas.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.4.0\" date=\"2006-08-30\">\n\n<change>\n<para lang=\"ru\">\nИзменение во внутреннем API: инициализация модулей HTTP перенесена из фазы\ninit module в фазу HTTP postconfiguration.\n</para>\n<para lang=\"en\">\nChange in internal API: the HTTP modules initialization was moved\nfrom the init module phase to the HTTP postconfiguration phase.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь тело запроса в модуле ngx_http_perl_module не считывается\nзаранее: нужно явно инициировать чтение с помощью метода $r->has_request_body.\n</para>\n<para lang=\"en\">\nnow the request body is not read beforehand for the ngx_http_perl_module:\nit's required to start the reading using the $r->has_request_body method.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает код возврата DECLINED.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the DECLINED return code.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module поддерживает входящую строку заголовка \"Date\"\nдля метода PUT.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module supports the incoming \"Date\" header line\nfor the PUT method.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssi работает внутри блока if.\n</para>\n<para lang=\"en\">\nthe \"ssi\" directive is available inside the \"if\" block.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроисходил segmentation fault, если в директиве index использовалась\nпеременные и при этом первое имя индексного файла было без переменных;\nошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\na segmentation fault occurred if there was an \"index\" directive with\nvariables and the first index name was without variables;\nthe bug had appeared in 0.1.29.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.61\" date=\"2006-08-28\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива tcp_nodelay теперь по умолчанию включена.\n</para>\n<para lang=\"en\">\nnow the \"tcp_nodelay\" directive is turned on by default.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива msie_refresh.\n</para>\n<para lang=\"en\">\nthe \"msie_refresh\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива recursive_error_pages.\n</para>\n<para lang=\"en\">\nthe \"recursive_error_pages\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива rewrite возвращала неправильный редирект, если редирект\nвключал в себя выделенные закодированные символы из оригинального URI.\n</para>\n<para lang=\"en\">\nthe \"rewrite\" directive returned incorrect redirect, if the redirect\nhad the captured escaped symbols from original URI.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.60\" date=\"2006-08-18\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво время перенаправления ошибки рабочий процесс мог зациклиться;\nошибка появилась в 0.3.59.\n</para>\n<para lang=\"en\">\na worker process may got caught in an endless loop\nwhile an error redirection;\nthe bug had appeared in 0.3.59.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.59\" date=\"2006-08-16\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь можно делать несколько перенаправлений через директиву error_page.\n</para>\n<para lang=\"en\">\nnow is possible to do several redirection using the \"error_page\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива dav_access не поддерживала три параметра.\n</para>\n<para lang=\"en\">\nthe \"dav_access\" directive did not support three parameters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива error_page не изменяла строку \"Content-Type\"\nпосле перенаправления с помощью \"X-Accel-Redirect\";\nошибка появилась в 0.3.58.\n</para>\n<para lang=\"en\">\nthe \"error_page\" directive did not changes the \"Content-Type\" header line\nafter the \"X-Accel-Redirect\" was used;\nthe bug had appeared in 0.3.58.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.58\" date=\"2006-08-14\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива error_page поддерживает переменные.\n</para>\n<para lang=\"en\">\nthe \"error_page\" directive supports the variables.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь на Linux используется интерфейс procfs вместо sysctl.\n</para>\n<para lang=\"en\">\nnow the procfs interface instead of sysctl is used on Linux.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь при использовании \"X-Accel-Redirect\" строка \"Content-Type\" наследуется\nиз первоначального ответа.\n</para>\n<para lang=\"en\">\nnow the \"Content-Type\" header line is inherited from first response\nwhen the \"X-Accel-Redirect\" was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива error_page не перенаправляла ошибку 413.\n</para>\n<para lang=\"en\">\nthe \"error_page\" directive did not redirect the 413 error.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзавершающий \"?\" не удалял старые аргументы, если в переписанном URI\nне было новых аргументов.\n</para>\n<para lang=\"en\">\nthe trailing \"?\" did not remove old arguments if no new arguments\nwere added to a rewritten URI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не запускался на 64-битной FreeBSD 7.0-CURRENT.\n</para>\n<para lang=\"en\">\nnginx could not run on 64-bit FreeBSD 7.0-CURRENT.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.57\" date=\"2006-08-09\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $ssl_client_serial.\n</para>\n<para lang=\"en\">\nthe $ssl_client_serial variable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв операторе \"!-e\" в директиве if.<br/>\nСпасибо Андриану Буданцову.\n</para>\n<para lang=\"en\">\nin the \"!-e\" operator of the \"if\" directive.<br/>\nThanks to Andrian Budanstov.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проверке клиентского сертификата nginx не передавал клиенту\nинформацию о требуемых сертификатах.\n</para>\n<para lang=\"en\">\nwhile a client certificate verification nginx did not send to a client\nthe required certificates information.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $document_root не поддерживала переменные в директиве root.\n</para>\n<para lang=\"en\">\nthe $document_root variable did not support the variables in the \"root\"\ndirective.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.56\" date=\"2006-08-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива dav_access.\n</para>\n<para lang=\"en\">\nthe \"dav_access\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива if поддерживает операторы \"-d\", \"!-d\", \"-e\", \"!-e\", \"-x\" и \"!-x\".\n</para>\n<para lang=\"en\">\nthe \"if\" directive supports the \"-d\", \"!-d\", \"-e\", \"!-e\", \"-x\", and \"!-x\"\noperators.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри записи в access_log некоторых передаваемых клиенту строк заголовков\nпроисходил segmentation fault, если запрос возвращал редирект.\n</para>\n<para lang=\"en\">\na segmentation fault occurred if a request returned a redirect and\nsome sent to client header lines were logged in the access log.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.55\" date=\"2006-07-28\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр stub в команде SSI include.\n</para>\n<para lang=\"en\">\nthe \"stub\" parameter in the \"include\" SSI command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nкоманда SSI block.\n</para>\n<para lang=\"en\">\nthe \"block\" SSI command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nскрипт unicode2nginx добавлен в contrib.\n</para>\n<para lang=\"en\">\nthe unicode2nginx script was added to contrib.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли root был задан только переменной, то корень задавался\nотносительно префикса сервера.\n</para>\n<para lang=\"en\">\nif a \"root\" was specified by variable only, then the root was relative\nto a server prefix.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в запросе был \"//\" или \"/.\", и после этого закодированные\nсимволы в виде \"%XX\", то проксируемый запрос передавался незакодированным.\n</para>\n<para lang=\"en\">\nif the request contained \"//\" or \"/./\" and escaped symbols after them,\nthen the proxied request was sent unescaped.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод $r->header_in(\"Cookie\") модуля ngx_http_perl_module теперь возвращает\nвсе строки \"Cookie\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nthe $r->header_in(\"Cookie\") of the ngx_http_perl_module now returns\nall \"Cookie\" header lines.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпроисходил segmentation fault, если использовался\n<nobr>\"client_body_in_file_only on\"</nobr>\nи делался переход к следующему бэкенду.\n</para>\n<para lang=\"en\">\na segmentation fault occurred if <nobr>\"client_body_in_file_only on\"</nobr>\nwas used and nginx switched to a next upstream.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри некоторых условиях во время переконфигурации коды символов\nвнутри директивы charset_map могли считаться неверными;\nошибка появилась в 0.3.50.\n</para>\n<para lang=\"en\">\non some condition while reconfiguration character codes\ninside the \"charset_map\" may be treated invalid;\nthe bug had appeared in 0.3.50.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.54\" date=\"2006-07-11\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nnginx теперь записывает в лог информацию о подзапросах.\n</para>\n<para lang=\"en\">\nnginx now logs the subrequest information to the error log.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_next_upstream, fastcgi_next_upstream и memcached_next_upstream\nподдерживают параметр off.\n</para>\n<para lang=\"en\">\nthe \"proxy_next_upstream\", \"fastcgi_next_upstream\",\nand \"memcached_next_upstream\" directives support the \"off\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива debug_connection поддерживает запись адресов в формате CIDR.\n</para>\n<para lang=\"en\">\nthe \"debug_connection\" directive supports the CIDR address form.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перекодировании ответа проксированного сервера или сервера FastCGI\nв UTF-8 или наоборот ответ мог передаваться не полностью.\n</para>\n<para lang=\"en\">\nif a response of proxied server or FastCGI server was converted from UTF-8\nor back, then it may be transferred incomplete.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $upstream_response_time содержала время только первого\nобращения к бэкенду.\n</para>\n<para lang=\"en\">\nthe $upstream_response_time variable had the time of the first\nrequest to a backend only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на платформе amd64;\nошибка появилась в 0.3.53.\n</para>\n<para lang=\"en\">\nnginx could not be built on amd64 platform;\nthe bug had appeared in 0.3.53.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.53\" date=\"2006-07-07\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива add_header добавляет строки в ответы с кодом 204, 301 и 302.\n</para>\n<para lang=\"en\">\nthe \"add_header\" directive adds the string to 204, 301, and 302 responses.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server в блоке upstream поддерживает параметр weight.\n</para>\n<para lang=\"en\">\nthe \"server\" directive in the \"upstream\" context supports\nthe \"weight\" parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_name поддерживает маску \"*\".\n</para>\n<para lang=\"en\">\nthe \"server_name\" directive supports the \"*\" wildcard.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nnginx поддерживает тело запроса больше 2G.\n</para>\n<para lang=\"en\">\nnginx supports the request body size more than 2G.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли при использовании \"satisfy_any on\" клиент успешно проходил аутентификацию,\nв лог всё равно записалоcь сообщение \"access forbidden by rule\".\n</para>\n<para lang=\"en\">\nif a client was successfully authorized using \"satisfy_any on\", then anyway\nthe message \"access forbidden by rule\" was written in the log.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nметод PUT мог ошибочно не создать файл и вернуть код 409.\n</para>\n<para lang=\"en\">\nthe \"PUT\" method may erroneously not create a file and return the 409 code.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли во время аутентификации IMAP/POP3 бэкенд возвращал ошибку, nginx\nпродолжал проксирование.\n</para>\n<para lang=\"en\">\nif the IMAP/POP3 backend returned an error, then nginx continued proxying\nanyway.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.52\" date=\"2006-07-03\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nвосстановлено поведение модуля ngx_http_index_module для запросов \"POST /\":\nкак в версии до 0.3.40, модуль теперь не выдаёт ошибку 405.\n</para>\n<para lang=\"en\">\nthe ngx_http_index_module behavior for the \"POST /\" requests is reverted\nto the 0.3.40 version state: the module now does not return the 405 error.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании ограничения скорости рабочий процесс мог зациклиться;\nошибка появилась в 0.3.37.\n</para>\n<para lang=\"en\">\nthe worker process may got caught in an endless loop if the limit rate was used;\nthe bug had appeared in 0.3.37.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_charset_module записывал в лог ошибку \"unknown charset\",\nдаже если перекодировка не требовалась;\nошибка появилась в 0.3.50.\n</para>\n<para lang=\"en\">\nngx_http_charset_module logged \"unknown charset\" alert, even if the recoding\nwas not needed;\nthe bug had appeared in 0.3.50.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в результате запроса PUT возвращался код 409, то временный файл\nне удалялся.\n</para>\n<para lang=\"en\">\nif a code response of the PUT request was 409, then a temporary file\nwas not removed.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.51\" date=\"2006-06-30\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри некоторых условиях в SSI мог пропадать символы \"&lt;\";\nошибка появилась в 0.3.50.\n</para>\n<para lang=\"en\">\nthe \"&lt;\" symbols might disappeared some conditions in the SSI;\nthe bug had appeared in 0.3.50.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.50\" date=\"2006-06-28\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы proxy_redirect_errors и fastcgi_redirect_errors\nпереименованы соответственно в proxy_intercept_errors и\nfastcgi_intercept_errors.\n</para>\n<para lang=\"en\">\nthe \"proxy_redirect_errors\" and \"fastcgi_redirect_errors\" directives\nwas renamed to the \"proxy_intercept_errors\" and\n\"fastcgi_intercept_errors\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_charset_module поддерживает перекодирование из\nоднобайтных кодировок в UTF-8 и обратно.\n</para>\n<para lang=\"en\">\nthe ngx_http_charset_module supports the recoding from the single byte\nencodings to the UTF-8 encoding and back.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв режиме прокси и FastCGI поддерживается строка заголовка \"X-Accel-Charset\"\nв ответе бэкенда.\n</para>\n<para lang=\"en\">\nthe \"X-Accel-Charset\" response header line is supported in proxy\nand FastCGI mode.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсимвол \"\\\" в парах \"\\\"\" и \"\\'\" в SSI командах убирался, только если\nтакже использовался символ \"$\".\n</para>\n<para lang=\"en\">\nthe \"\\\" escape symbol in the \"\\\"\" and \"\\'\" pairs in the SSI command\nwas removed only if the command also has the \"$\" symbol.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри некоторых условиях в SSI после вставки могла быть добавлена\nстрока \"&lt;!--\".\n</para>\n<para lang=\"en\">\nthe \"&lt;!--\" string might be added on some conditions\nin the SSI after inclusion.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в заголовке ответа была строка <nobr>\"Content-Length: 0\",</nobr>\nто при использовании небуферизированного проксировании не закрывалось соединение\nс клиентом.\n</para>\n<para lang=\"en\">\nif the \"Content-Length: 0\" header line was in response, then\nin nonbuffered proxying mode the client connection was not closed.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.49\" date=\"2006-05-31\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве set.\n</para>\n<para lang=\"en\">\nin the \"set\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включении в ssi двух и более подзапросов, обрабатываемых через FastCGI,\nвместо вывода второго и остальных подзапросов в ответ включался вывод\nпервого подзапроса.\n</para>\n<para lang=\"en\">\nif two or more FastCGI subrequests was in SSI, then first subrequest output\nwas included instead of second and following subrequests.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.48\" date=\"2006-05-29\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь модуль ngx_http_charset_module работает для подзапросов,\nв ответах которых нет строки заголовка \"Content-Type\".\n</para>\n<para lang=\"en\">\nnow the ngx_http_charset_module works for subrequests,\nif the response has no \"Content-Type\" header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве proxy_pass не было URI,\nто директива \"proxy_redirect  default\" добавляла в переписанный\nредирект в начало лишний слэш.\n</para>\n<para lang=\"en\">\nif the \"proxy_pass\" directive has no URI part,\nthen the \"proxy_redirect  default\" directive add the unnecessary slash\nin start of the rewritten redirect.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nвнутренний редирект всегда превращал любой HTTP-метод в GET,\nтеперь это делается только для редиректов, выполняемых с помощью\nX-Accel-Redirect, и у которых метод не равен HEAD;\nошибка появилась в 0.3.42.\n</para>\n<para lang=\"en\">\nthe internal redirect always transform client's HTTP method to GET,\nnow the transformation is made for the \"X-Accel-Redirect\" redirects only\nand if the method is not HEAD;\nthe bug had appeared in 0.3.42.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module не собирался, если перл был с поддержкой потоков;\nошибка появилась в 0.3.46.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module could not be built, if the perl was built\nwith the threads support;\nthe bug had appeared in 0.3.46.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.47\" date=\"2006-05-23\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива upstream.\n</para>\n<para lang=\"en\">\nthe \"upstream\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nсимвол \"\\\" в парах \"\\\"\" и \"\\'\" в SSI командах теперь всегда убирается.\n</para>\n<para lang=\"en\">\nnow the \"\\\" escape symbol in the \"\\\"\" and \"\\'\" pairs in the SSI command\nis always removed.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.46\" date=\"2006-05-11\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_hide_header, proxy_pass_header, fastcgi_hide_header\nи fastcgi_pass_header.\n</para>\n<para lang=\"en\">\nthe \"proxy_hide_header\", \"proxy_pass_header\", \"fastcgi_hide_header\",\nand \"fastcgi_pass_header\" directives.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы proxy_pass_x_powered_by, fastcgi_x_powered_by и proxy_pass_server\nупразднены.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass_x_powered_by\", \"fastcgi_x_powered_by\", and \"proxy_pass_server\"\ndirectives were canceled.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв режиме прокси поддерживается строка заголовка \"X-Accel-Buffering\"\nв ответе бэкенда.\n</para>\n<para lang=\"en\">\nthe \"X-Accel-Buffering\" response header line is supported in proxy mode.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибок и утечек памяти при переконфигурации в модуле ngx_http_perl_module.\n</para>\n<para lang=\"en\">\nthe reconfiguration bug and memory leaks in the ngx_http_perl_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.45\" date=\"2006-05-06\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssl_verify_client, ssl_verify_depth и ssl_client_certificate.\n</para>\n<para lang=\"en\">\nthe \"ssl_verify_client\", \"ssl_verify_depth\", and \"ssl_client_certificate\"\ndirectives.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь переменная $request_method возвращает метод только основного запроса.\n</para>\n<para lang=\"en\">\nthe $request_method variable now returns the main request method.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв таблице перекодировки koi-win изменены коды символа &amp;deg;.\n</para>\n<para lang=\"en\">\nthe &amp;deg; symbol codes were changed in koi-win conversion table.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв таблицу перекодировки koi-win добавлены символы евро и номера.\n</para>\n<para lang=\"en\">\nthe euro and N symbols were added to koi-win conversion table.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли nginx распределял запросы на несколько машин, то при падении\nодной из них запросы, предназначенные для этой машины, перенаправлялись только\nна одну машину вместо того, чтобы равномерно распределяться между остальными.\n</para>\n<para lang=\"en\">\nif nginx distributed the requests among several backends and some backend\nfailed, then requests intended for this backend was directed to one live\nbackend only instead of being distributed among the rest.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.44\" date=\"2006-05-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр wait в команде SSI include.\n</para>\n<para lang=\"en\">\nthe \"wait\" parameter in the \"include\" SSI command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв таблицу перекодировки koi-win добавлены украинские и белорусские символы.\n</para>\n<para lang=\"en\">\nthe Ukrainian and Byelorussian characters were added to koi-win conversion\ntable.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв SSI.\n</para>\n<para lang=\"en\">\nin the SSI.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.43\" date=\"2006-04-26\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв SSI.\n</para>\n<para lang=\"en\">\nin the SSI.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.42\" date=\"2006-04-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр bind в директиве listen в IMAP/POP3 прокси.\n</para>\n<para lang=\"en\">\nthe \"bind\" option of the \"listen\" directive in IMAP/POP3 proxy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании в директиве rewrite одного и того же\nвыделения более одного раза.\n</para>\n<para lang=\"en\">\nif the same capture in the \"rewrite\" directive was used more then once.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв лог не записывались переменные\n$sent_http_content_type, $sent_http_content_length, $sent_http_last_modified,\n$sent_http_connection, $sent_http_keep_alive и $sent_http_transfer_encoding.\n</para>\n<para lang=\"en\">\nthe $sent_http_content_type, $sent_http_content_length,\n$sent_http_last_modified, $sent_http_connection, $sent_http_keep_alive,\nand $sent_http_transfer_encoding variables were not written to access log.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременная $sent_http_cache_control возвращала содержимое только одной\nстроки \"Cache-Control\" в заголовке ответа.\n</para>\n<para lang=\"en\">\nthe $sent_http_cache_control returned value of the single \"Cache-Control\"\nresponse header line.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.41\" date=\"2006-04-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -v.\n</para>\n<para lang=\"en\">\nthe -v switch.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включении в SSI удалённых подзапросов\nмог произойти segmentation fault.\n</para>\n<para lang=\"en\">\nthe segmentation fault may occurred if the SSI page has remote subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв обработке FastCGI.\n</para>\n<para lang=\"en\">\nin FastCGI handling.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли путь к перловым модулям не был указан с помощью\n--with-perl_modules_path=PATH или директивы perl_modules,\nто на старте происходил segmentation fault.\n</para>\n<para lang=\"en\">\nif the perl modules path was not set using\n--with-perl_modules_path=PATH or the \"perl_modules\", then\nthe segmentation fault was occurred.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.40\" date=\"2006-04-19\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module поддерживает метод MKCOL.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module supports the MKCOL method.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива create_full_put_path.\n</para>\n<para lang=\"en\">\nthe \"create_full_put_path\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $limit_rate.\n</para>\n<para lang=\"en\">\nthe \"$limit_rate\" variable.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.39\" date=\"2006-04-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива uninitialized_variable_warn; уровень логгирования сообщения\nо неинициализированной переменной понижен с уровня alert на warn.\n</para>\n<para lang=\"en\">\nthe \"uninitialized_variable_warn\" directive; the logging level of the\n\"uninitialized variable\" message was lowered from \"alert\" to \"warn\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива override_charset.\n</para>\n<para lang=\"en\">\nthe \"override_charset\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпри использовании неизвестной переменной в SSI-командах echo и if expr='$name'\nтеперь не записывается в лог сообщение о неизвестной переменной.\n</para>\n<para lang=\"en\">\nnow if the unknown variable is used in the \"echo\" and \"if expr='$name'\"\nSSI-commands, then the \"unknown variable\" message is not logged.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсчётчик активных соединений рос при превышении лимита соединений,\nзаданного директивой worker_connections;\nошибка появилась в 0.2.0.\n</para>\n<para lang=\"en\">\nthe active connection counter increased on the exceeding of the connection\nlimit specified by the \"worker_connections\" directive;\nthe bug had appeared in 0.2.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри некоторых условия ограничение скорости соединения могло не работать;\nошибка появилась в 0.3.38.\n</para>\n<para lang=\"en\">\nthe limit rate might not work on some condition;\nthe bug had appeared in 0.3.38.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.38\" date=\"2006-04-14\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_dav_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_dav_module.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nоптимизация модуля ngx_http_perl_module.<br/>\nСпасибо Сергею Скворцову.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module optimizations.<br/>\nThanks to Sergey Skvortsov.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает метод $r->request_body_file.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->request_body_file method.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива client_body_in_file_only.\n</para>\n<para lang=\"en\">\nthe \"client_body_in_file_only\" directive.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nтеперь при переполнении диска nginx пытается писать access_log'и только\nраз в секунду.<br/>\nСпасибо Антону Южанинову и Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow on disk overflow nginx tries to write access logs once a second only.<br/>\nThanks to Anton Yuzhaninov and Maxim Dounin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтеперь директива limit_rate точнее ограничивает скорость при значениях\nбольше <nobr>100 Kbyte/s.</nobr><br/>\nСпасибо ForJest.\n</para>\n<para lang=\"en\">\nnow the \"limit_rate\" directive more precisely limits rate if rate is more\nthan <nobr>100 Kbyte/s.</nobr><br/>\nThanks to ForJest.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nIMAP/POP3 прокси теперь передаёт серверу авторизации символы \"\\r\" и \"\\n\"\nв логине и пароле в закодированном виде.<br/>\nСпасибо Максиму Дунину.\n</para>\n<para lang=\"en\">\nnow the IMAP/POP3 proxy escapes the \"\\r\" and \"\\n\" symbols in login and\npassword to pass authorization server.<br/>\nThanks to Maxim Dounin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.37\" date=\"2006-04-07\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива limit_except.\n</para>\n<para lang=\"en\">\nthe \"limit_except\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива if поддерживает операторы \"!~\", \"!~*\", \"-f\" и \"!-f\".\n</para>\n<para lang=\"en\">\nthe \"if\" directive supports the \"!~\", \"!~*\", \"-f\", and \"!-f\" operators.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает метод $r->request_body.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->request_body method.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв модуле ngx_http_addition_filter_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_addition_filter_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.36\" date=\"2006-04-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_addition_filter_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_addition_filter_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_pass и fastcgi_pass можно использовать внутри блока if.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\" and \"fastcgi_pass\" directives may be used inside\nthe \"if\" block.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_ignore_client_abort и fastcgi_ignore_client_abort.\n</para>\n<para lang=\"en\">\nthe \"proxy_ignore_client_abort\" and \"fastcgi_ignore_client_abort\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $request_completion.\n</para>\n<para lang=\"en\">\nthe \"$request_completion\" variable.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает методы $r->request_method и\n$r->remote_addr.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->request_method and $r->remote_addr.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает команду elif.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports the \"elif\" command.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстрока \"\\/\" в начале выражения команды if модуля ngx_http_ssi_module\nвоспринималась неверно.\n</para>\n<para lang=\"en\">\nthe \"\\/\" string in the expression of the \"if\" command of the\nngx_http_ssi_module was treated incorrectly.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв использовании регулярных выражениях в команде if модуля ngx_http_ssi_module.\n</para>\n<para lang=\"en\">\nin the regular expressions in the \"if\" command of the ngx_http_ssi_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри задании относительного пути в директивах\nclient_body_temp_path, proxy_temp_path, fastcgi_temp_path и perl_modules\nиспользовался каталог относительно текущего каталога, а не относительно\nпрефикса сервера.\n</para>\n<para lang=\"en\">\nif the relative path was specified in the \"client_body_temp_path\",\n\"proxy_temp_path\", \"fastcgi_temp_path\", and \"perl_modules\" directives,\nthen the directory was used relatively to a current path but not\nto a server prefix.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.35\" date=\"2006-03-22\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\naccept-фильтр и TCP_DEFER_ACCEPT устанавливались только для первой\nдирективы listen;\nошибка появилась в 0.3.31.\n</para>\n<para lang=\"en\">\nthe accept-filter and the TCP_DEFER_ACCEPT option were set for first \"listen\"\ndirective only;\nthe bug had appeared in 0.3.31.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве proxy_pass без URI при использовании в подзапросе.\n</para>\n<para lang=\"en\">\nin the \"proxy_pass\" directive without the URI part in a subrequest.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.34\" date=\"2006-03-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива add_header поддерживает переменные.\n</para>\n<para lang=\"en\">\nthe \"add_header\" directive supports the variables.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.33\" date=\"2006-03-15\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр http_503 в директивах proxy_next_upstream или fastcgi_next_upstream.\n</para>\n<para lang=\"en\">\nthe \"http_503\" parameter of the \"proxy_next_upstream\" or\n\"fastcgi_next_upstream\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nngx_http_perl_module не работал со встроенным в конфигурационный файл кодом,\nесли он не начинался сразу же с \"sub\".\n</para>\n<para lang=\"en\">\nngx_http_perl_module did not work with inlined in the configuration code,\nif it was not started with the \"sub\" word.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве post_action.\n</para>\n<para lang=\"en\">\nin the \"post_action\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.32\" date=\"2006-03-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nудаление отладочного логгирования на старте и при переконфигурации;\nошибка появилась в 0.3.31.\n</para>\n<para lang=\"en\">\nthe debug logging on startup and reconfiguration time was removed;\nthe bug had appeared in 0.3.31.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.31\" date=\"2006-03-10\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь nginx передаёт неверные ответы проксированного бэкенда.\n</para>\n<para lang=\"en\">\nnow nginx passes the malformed proxied backend responses.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы listen поддерживают адрес в виде \"*:порт\".\n</para>\n<para lang=\"en\">\nthe \"listen\" directives support the address in the \"*:port\" form.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка EVFILER_TIMER в MacOSX 10.4.\n</para>\n<para lang=\"en\">\nthe EVFILER_TIMER support in MacOSX 10.4.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nобход ошибки обработки миллисекундных таймаутов kqueue в 64-битном ядре\nMacOSX.<br/>\nСпасибо Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nfor MacOSX 64-bit kernel kqueue millisecond timeout bug.<br/>\nThanks to Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли внутри одного сервера описаны несколько директив listen, слушающих на\nразных адресах, то имена серверов вида \"*.domain.tld\" работали только\nдля первого адреса;\nошибка появилась в 0.3.18.\n</para>\n<para lang=\"en\">\nif there were several \"listen\" directives listening one various addresses\ninside one server, then server names like \"*.domain.tld\" worked for first\naddress only;\nthe bug had appeared in 0.3.18.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании протокола HTTPS в директиве proxy_pass не передавались\nзапросы с телом, записанным во временный файл.\n</para>\n<para lang=\"en\">\nif the HTTPS protocol was used in the \"proxy_pass\" directive and\nthe request body was in temporary file then the request was not transferred.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с perl 5.8.8.\n</para>\n<para lang=\"en\">\nperl 5.8.8 compatibility.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.30\" date=\"2006-02-22\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nуровень записи в лог ошибки ECONNABORTED изменён на error с уровня crit.\n</para>\n<para lang=\"en\">\nthe ECONNABORTED error log level was changed to \"error\" from \"crit\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module не собирался без модуля ngx_http_ssi_filter_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module could not be build without\nthe ngx_http_ssi_filter_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на i386 платформе, если использовался PIC;\nошибка появилась в 0.3.27.\n</para>\n<para lang=\"en\">\nnginx could not be built on i386 platform, if the PIC was used;\nthe bug had appeared in 0.3.27.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.29\" date=\"2006-02-20\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nтеперь nginx использует меньше памяти, если PHP в режиме FastCGI передаёт\nбольшое количество предупреждений перед ответом.\n</para>\n<para lang=\"en\">\nnow nginx uses less memory, if PHP in FastCGI mode sends many warnings\nbefore the response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв ответах 204 для запросов версии HTTP/1.1 выдавалась строка заголовка\n\"Transfer-Encoding: chunked\".\n</para>\n<para lang=\"en\">\nthe \"Transfer-Encoding: chunked\" header line was issued in the 204 responses\nfor the HTTP/1.1 requests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx возвращал 502 код ответа, если FastCGI сервер передавал полные строки\nзаголовка ответа в отдельных FastCGI записях.\n</para>\n<para lang=\"en\">\nnginx returned the 502 response, if the complete response header lines\nwere transferred in a separate FastCGI records.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве post_action был указан проксируемый URI, то он выполнялся\nтолько после успешного завершения запроса.\n</para>\n<para lang=\"en\">\nif the proxied URI was specified in the \"post_action\" directive, then it ran\nonly after a successful completion of a request.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.28\" date=\"2006-02-16\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива restrict_host_names упразднена.\n</para>\n<para lang=\"en\">\nthe \"restrict_host_names\" directive was canceled.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр конфигурации --with-cpu-opt=ppc64.\n</para>\n<para lang=\"en\">\nthe --with-cpu-opt=ppc64 configuration parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри некоторых условиях проксированное соединение с клиентом завершалось\nпреждевременно.<br/>\nСпасибо Владимиру Шутову.\n</para>\n<para lang=\"en\">\non some condition the proxied connection with a client was terminated\nprematurely.<br/>\nThanks to Vladimir Shutoff.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстрока заголовка \"X-Accel-Limit-Rate\" не учитывалась для запросов,\nперенаправленных с помощью строки \"X-Accel-Redirect\".\n</para>\n<para lang=\"en\">\nthe \"X-Accel-Limit-Rate\" header line was not taken into account\nif the request was redirected using the \"X-Accel-Redirect\" header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива post_action работала только после успешного завершения запроса.\n</para>\n<para lang=\"en\">\nthe \"post_action\" directive ran only after a successful completion of a request.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nтело проксированного ответа, создаваемого директивой post_action,\nпередавалось клиенту.\n</para>\n<para lang=\"en\">\nthe proxied response body generated by the \"post_action\" directive\nwas transferred to a client.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.27\" date=\"2006-02-08\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы variables_hash_max_size и variables_hash_bucket_size.\n</para>\n<para lang=\"en\">\nthe \"variables_hash_max_size\" and \"variables_hash_bucket_size\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременная $body_bytes_sent доступна не только в директиве log_format.\n</para>\n<para lang=\"en\">\nthe $body_bytes_sent variable can be used not only in the \"log_format\"\ndirective.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные $ssl_protocol и $ssl_cipher.\n</para>\n<para lang=\"en\">\nthe $ssl_protocol and $ssl_cipher variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nопределение размера строки кэша распространённых процессоров при старте.\n</para>\n<para lang=\"en\">\nthe cache line size detection for widespread CPUs at start time.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива accept_mutex теперь поддерживается посредством fcntl(2)\nна платформах, отличных от i386, amd64, sparc64 и ppc.\n</para>\n<para lang=\"en\">\nnow the \"accept_mutex\" directive is supported using fcntl(2)\non platforms different from i386, amd64, sparc64, and ppc.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива lock_file и параметр автоконфигурации --with-lock-path=PATH.\n</para>\n<para lang=\"en\">\nthe \"lock_file\" directive and the --with-lock-path=PATH autoconfiguration\ndirective.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании протокола HTTPS в директиве proxy_pass не передавались\nзапросы с телом.\n</para>\n<para lang=\"en\">\nif the HTTPS protocol was used in the \"proxy_pass\" directive then\nthe requests with the body was not transferred.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.26\" date=\"2006-02-03\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива optimize_host_names переименована в optimize_server_names.\n</para>\n<para lang=\"en\">\nthe \"optimize_host_names\" directive was renamed to the \"optimize_server_names\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании подзапроса в SSI бэкенду передавался URI основного запроса,\nесли в директиве proxy_pass отсутствовал URI.\n</para>\n<para lang=\"en\">\nif in the \"proxy_pass\" directive was no the URI part, then the main request\nURI was transferred to a backend while proxying the SSI subrequest.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.25\" date=\"2006-02-01\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри неверной конфигурации на старте или во время переконфигурации происходил\nsegmentation fault;\nошибка появилась в 0.3.24.\n</para>\n<para lang=\"en\">\nthe segmentation fault was occurred on start or while reconfiguration\nif there was invalid configuration;\nthe bug had appeared in 0.3.24.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.24\" date=\"2006-02-01\">\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nобход ошибки в kqueue во FreeBSD.\n</para>\n<para lang=\"en\">\nfor bug in FreeBSD kqueue.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответ, создаваемый директивой post_action, теперь не передаётся клиенту.\n</para>\n<para lang=\"en\">\nnow a response generated by the \"post_action\" directive is not transferred\nto a client.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании большого количества лог-файлов происходила утечка памяти.\n</para>\n<para lang=\"en\">\nthe memory leaks were occurring if many log files were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nвнутри одного location работала только первая директива proxy_redirect.\n</para>\n<para lang=\"en\">\nthe first \"proxy_redirect\" directive was working inside one location.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна 64-битных платформах при старте мог произойти segmentation fault,\nесли использовалось большое количество имён в директивах server_name;\nошибка появилась в 0.3.18.\n</para>\n<para lang=\"en\">\non 64-bit platforms segmentation fault may occurred on start\nif the many names were used in the \"server_name\" directives;\nthe bug had appeared in 0.3.18.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.23\" date=\"2006-01-24\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива optimize_host_names.\n</para>\n<para lang=\"en\">\nthe \"optimize_host_names\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании переменных в директивах path и alias.\n</para>\n<para lang=\"en\">\nin using of the variables in the \"path\" and \"alias\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module неправильно собирался на Linux и Solaris.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module was incorrectly built on Linux and Solaris.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.22\" date=\"2006-01-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module поддерживает методы $r->args и $r->unescape.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module supports the $r->args and $r->unescape methods.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nметод $r->query_string в модуле ngx_http_perl_module упразднён.\n</para>\n<para lang=\"en\">\nthe method $r->query_string of ngx_http_perl_module was canceled.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве valid_referers указаны только none или blocked, то\nпроисходил segmentation fault;\nошибка появилась в 0.3.18.\n</para>\n<para lang=\"en\">\nsegmentation fault was occurred if the \"none\" or \"blocked\" values was\nspecified in the \"valid_referers\" directive;\nthe bug had appeared in 0.3.18.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.21\" date=\"2006-01-16\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_perl_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_perl_module.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива valid_referers разрешает использовать рефереры совсем без URI.\n</para>\n<para lang=\"en\">\nthe \"valid_referers\" directive allows the referrers without URI part.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.20\" date=\"2006-01-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки в обработке SSI.\n</para>\n<para lang=\"en\">\nin SSI handling.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_memcached_module не поддерживал ключи в виде /uri?args.\n</para>\n<para lang=\"en\">\nthe ngx_http_memcached_module did not support the keys in the \"/usr?args\" form.\n</para>\n</change>\n\n</changes>\n\n<changes ver=\"0.3.19\" date=\"2005-12-28\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы path и alias поддерживают переменные.\n</para>\n<para lang=\"en\">\nthe \"path\" and \"alias\" directives support the variables.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директива valid_referers опять учитывает URI.\n</para>\n<para lang=\"en\">\nnow the \"valid_referers\" directive again checks the URI part.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки в обработке SSI.\n</para>\n<para lang=\"en\">\nin SSI handling.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.18\" date=\"2005-12-26\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_names поддерживает имена вида \".domain.tld\".\n</para>\n<para lang=\"en\">\nthe \"server_names\" directive supports the \".domain.tld\" names.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_names использует хэш для имён вида \"*.domain.tld\"\nи более эффективный хэш для обычных имён.\n</para>\n<para lang=\"en\">\nthe \"server_names\" directive uses the hash for the \"*.domain.tld\" names\nand more effective hash for usual names.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы server_names_hash_max_size и server_names_hash_bucket_size.\n</para>\n<para lang=\"en\">\nthe \"server_names_hash_max_size\" and \"server_names_hash_bucket_size\" directives.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы server_names_hash и server_names_hash_threshold упразднены.\n</para>\n<para lang=\"en\">\nthe \"server_names_hash\" and \"server_names_hash_threshold\" directives\nwere canceled.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива valid_referers использует хэш для имён сайтов.\n</para>\n<para lang=\"en\">\nthe \"valid_referers\" directive uses the hash site names.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтеперь директива valid_referers проверяет только имена сайтов без учёта URI.\n</para>\n<para lang=\"en\">\nnow the \"valid_referers\" directive checks the site names only without\nthe URI part.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнекоторые имена вида \".domain.tld\" неверно обрабатывались модулем\nngx_http_map_module.\n</para>\n<para lang=\"en\">\nsome \".domain.tld\" names incorrectly processed by the ngx_http_map_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли конфигурационного файла не было, то происходил segmentation fault;\nошибка появилась в 0.3.12.\n</para>\n<para lang=\"en\">\nsegmentation fault was occurred if configuration file did not exist;\nthe bug had appeared in 0.3.12.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна 64-битных платформах при старте мог произойти segmentation fault;\nошибка появилась в 0.3.16.\n</para>\n<para lang=\"en\">\non 64-bit platforms segmentation fault may occurred on start;\nthe bug had appeared in 0.3.16.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.17\" date=\"2005-12-18\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nна Linux configure теперь проверяет наличие epoll и sendfile64() в ядре.\n</para>\n<para lang=\"en\">\nnow on Linux configure checks the presence of epoll and sendfile64() in kernel.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива map поддерживает доменные имена в формате \".domain.tld\".\n</para>\n<para lang=\"en\">\nthe \"map\" directive supports domain names in the \".domain.tld\" form.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво время SSL handshake не иcпользовались таймауты;\nошибка появилась в 0.2.4.\n</para>\n<para lang=\"en\">\nthe timeouts were not used in SSL handshake;\nthe bug had appeared in 0.2.4.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв использовании протокола HTTPS в директиве proxy_pass.\n</para>\n<para lang=\"en\">\nin the HTTPS protocol in the \"proxy_pass\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании протокола HTTPS в директиве proxy_pass по умолчанию\nиспользовался порт 80.\n</para>\n<para lang=\"en\">\nwhen the HTTPS protocol was used in the \"proxy_pass\" directive the port 80\nwas used by default.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.16\" date=\"2005-12-16\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_map_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_map_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы types_hash_max_size и types_hash_bucket_size.\n</para>\n<para lang=\"en\">\nthe \"types_hash_max_size\" and \"types_hash_bucket_size\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssi_value_length.\n</para>\n<para lang=\"en\">\nthe \"ssi_value_length\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_rlimit_core.\n</para>\n<para lang=\"en\">\nthe \"worker_rlimit_core\" directive.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nпри сборке компиляторами icc 8.1 и 9.0 с оптимизацией для\n<nobr>Pentium 4</nobr> номер соединения в логах всегда был равен 1.\n</para>\n<para lang=\"en\">\nthe connection number in logs was always 1 if nginx was built by the\nicc 8.1 or 9.0 compilers with optimization for <nobr>Pentium 4.</nobr>\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nкоманда config timefmt в SSI задавала неверный формат времени.\n</para>\n<para lang=\"en\">\nthe \"config timefmt\" SSI command set incorrect time format.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не закрывал соединения с IMAP/POP3 бэкендом при использовании SSL\nсоединений;\nошибка появилась в 0.3.13.<br/>\nСпасибо Rob Mueller.\n</para>\n<para lang=\"en\">\nnginx did not close connection to IMAP/POP3 backend for the SSL\nconnections;\nthe bug had appeared in 0.3.13.<br/>\nThanks to Rob Mueller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nsegmentation fault мог произойти во время SSL shutdown;\nошибка появилась в 0.3.13.\n</para>\n<para lang=\"en\">\nsegmentation fault may occurred in at SSL shutdown;\nthe bug had appeared in 0.3.13.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.15\" date=\"2005-12-07\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновой код 444 в директиве return для закрытия соединения.\n</para>\n<para lang=\"en\">\nthe new 444 code of the \"return\" directive to close connection.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива so_keepalive в IMAP/POP3 прокси.\n</para>\n<para lang=\"en\">\nthe \"so_keepalive\" directive in IMAP/POP3 proxy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx теперь вызывает abort() при обнаружении незакрытых соединений\nтолько при плавном выходе и включённой директиве debug_points.\n</para>\n<para lang=\"en\">\nif there are unclosed connection nginx now calls abort() only on graceful\nquit and active \"debug_points\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.14\" date=\"2005-12-05\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв ответе 304 передавалось тело ответа;\nошибка появилась в 0.3.13.\n</para>\n<para lang=\"en\">\nin the 304 response the body was transferred;\nthe bug had appeared in 0.3.13.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.13\" date=\"2005-12-05\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nIMAP/POP3 прокси поддерживает STARTTLS и STLS.\n</para>\n<para lang=\"en\">\nthe IMAP/POP3 proxy supports STARTTLS and STLS.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nIMAP/POP3 прокси не работала с методами select, poll и /dev/poll.\n</para>\n<para lang=\"en\">\nthe IMAP/POP3 proxy did not work with the select, poll, and /dev/poll methods.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки в обработке SSI.\n</para>\n<para lang=\"en\">\nin SSI handling.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nsendfilev() в Solaris теперь не используется при передаче тела запроса\nFastCGI-серверу через unix domain сокет.\n</para>\n<para lang=\"en\">\nnow Solaris sendfilev() is not used to transfer the client request body\nto FastCGI-server via the unix domain socket.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива auth_basic не запрещала аутентификацию;\nошибка появилась в 0.3.11.\n</para>\n<para lang=\"en\">\nthe \"auth_basic\" directive did not disable the authorization;\nthe bug had appeared in 0.3.11.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.12\" date=\"2005-11-26\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nесли nginx был собран с модулем ngx_http_realip_module, то при использовании\nдирективы \"satisfy_any on\" директивы доступа и аутентификации не работали.\nМодуль ngx_http_realip_module не собирался и не собирается по умолчанию.\n</para>\n<para lang=\"en\">\nif nginx was built with the ngx_http_realip_module and the \"satisfy_any on\"\ndirective was used, then access and authorization directives did not work.\nThe ngx_http_realip_module was not built and is not built by default.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nимя переменной \"$time_gmt\" изменено на \"$time_local\".\n</para>\n<para lang=\"en\">\nthe \"$time_gmt\" variable name was changed to \"$time_local\".\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы proxy_header_buffer_size и fastcgi_header_buffer_size\nпереименованы соответственно в proxy_buffer_size и fastcgi_buffer_size.\n</para>\n<para lang=\"en\">\nthe \"proxy_header_buffer_size\" and \"fastcgi_header_buffer_size\" directives\nwas renamed to the \"proxy_buffer_size\" and \"fastcgi_buffer_size\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_memcached_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_memcached_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_buffering.\n</para>\n<para lang=\"en\">\nthe \"proxy_buffering\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nизменение в работе с accept mutex при использовании метода rtsig;\nошибка появилась в 0.3.0.\n</para>\n<para lang=\"en\">\nthe changes in accept mutex  handling when the \"rtsig\" method was used;\nthe bug had appeared in 0.3.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли клиент передал строку \"Transfer-Encoding: chunked\" в заголовке\nзапроса, то nginx теперь выдаёт ошибку 411.\n</para>\n<para lang=\"en\">\nif the client sent the \"Transfer-Encoding: chunked\" header line, then\nnginx returns the 411 error.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри наследовании директивы auth_basic с уровня http в строке\n\"WWW-Authenticate\" заголовка ответа выводился realm без текста \"Basic realm\".\n</para>\n<para lang=\"en\">\nif the \"auth_basic\" directive was inherited from the http level,\nthen the realm in the \"WWW-Authenticate\" header line was without\nthe \"Basic realm\" text.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве access_log был явно указан формат combined, то в лог\nзаписывались пустые строки;\nошибка появилась в 0.3.8.\n</para>\n<para lang=\"en\">\nif the \"combined\" format was explicitly specified in the \"access_log\" directive,\nthen the empty lines was written to the log;\nthe bug had appeared in 0.3.8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не работал на платформе sparc под любыми OS, кроме Solaris.\n</para>\n<para lang=\"en\">\nnginx did not run on the sparc platform under any OS except Solaris.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве if теперь не нужно разделять пробелом строку в кавычках и\nзакрывающую скобку.\n</para>\n<para lang=\"en\">\nnow it is not necessary to place space between the quoted string and closing\nbracket in the \"if\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.11\" date=\"2005-11-15\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не передавал при проксировании тело запроса и строки заголовка клиента;\nошибка появилась в 0.3.10.\n</para>\n<para lang=\"en\">\nnginx did not pass the client request headers and body while proxying;\nthe bug had appeared in 0.3.10.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.10\" date=\"2005-11-15\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива valid_referers и переменная $invalid_referer перенесены\nиз модуля ngx_http_rewrite_module в новый модуль ngx_http_referer_module.\n</para>\n<para lang=\"en\">\nthe \"valid_referers\" directive and the \"$invalid_referer\" variable\nwere moved to the new ngx_http_referer_module from the ngx_http_rewrite_module.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nимя переменной \"$apache_bytes_sent\" изменено на \"$body_bytes_sent\".\n</para>\n<para lang=\"en\">\nthe \"$apache_bytes_sent\" variable name was changed to \"$body_bytes_sent\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные \"$sent_http_...\".\n</para>\n<para lang=\"en\">\nthe \"$sent_http_...\" variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива if поддерживает операции \"=\" и \"!=\".\n</para>\n<para lang=\"en\">\nthe \"if\" directive supports the \"=\" and \"!=\" operations.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_pass поддерживает протокол HTTPS.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\" directive supports the HTTPS protocol.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_set_body.\n</para>\n<para lang=\"en\">\nthe \"proxy_set_body\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива post_action.\n</para>\n<para lang=\"en\">\nthe \"post_action\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_empty_gif_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_empty_gif_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_cpu_affinity для Linux.\n</para>\n<para lang=\"en\">\nthe \"worker_cpu_affinity\" directive for Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива rewrite не раскодировала символы в редиректах в URI,\nтеперь символы раскодируются, кроме символов %00-%25 и %7F-%FF.\n</para>\n<para lang=\"en\">\nthe \"rewrite\" directive did not unescape URI part in redirect,\nnow it is unescaped except the %00-%25 and %7F-%FF characters.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался компилятором icc 9.0.\n</para>\n<para lang=\"en\">\nnginx could not be built by the icc 9.0 compiler.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли для статического файла нулевого размера был разрешён SSI,\nто ответ передавался неверно при кодировании chunk'ами.\n</para>\n<para lang=\"en\">\nif the SSI was enabled for zero size static file, then the chunked\nresponse was encoded incorrectly.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.9\" date=\"2005-11-10\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx считал небезопасными URI, в которых между двумя слэшами\nнаходилось два любых символа;\nошибка появилась в 0.3.8.\n</para>\n<para lang=\"en\">\nnginx considered URI as unsafe if two any symbols was between two slashes;\nthe bug had appeared in 0.3.8.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.8\" date=\"2005-11-09\">\n\n<change type=\"security\">\n<para lang=\"ru\">\nnginx теперь проверят URI, полученные от бэкенда в строке \"X-Accel-Redirect\"\nв заголовке ответа, или в SSI файле на наличие путей \"/../\" и нулей.\n</para>\n<para lang=\"en\">\nnginx now checks URI got from a backend in \"X-Accel-Redirect\" header line\nor in SSI file for the \"/../\" paths and zeroes.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nnginx теперь не воспринимает пустое имя как правильное\nв строке \"Authorization\" в заголовке запроса.\n</para>\n<para lang=\"en\">\nnginx now does not treat the empty user name in the \"Authorization\" header\nline as valid one.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssl_session_timeout модулей\nngx_http_ssl_module и ngx_imap_ssl_module.\n</para>\n<para lang=\"en\">\nthe \"ssl_session_timeout\" directives\nof the ngx_http_ssl_module and ngx_imap_ssl_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива auth_http_header модуля ngx_imap_auth_http_module.\n</para>\n<para lang=\"en\">\nthe \"auth_http_header\" directive of the ngx_imap_auth_http_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива add_header.\n</para>\n<para lang=\"en\">\nthe \"add_header\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_realip_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_realip_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновые переменные для использования в директиве log_format:\n$bytes_sent, $apache_bytes_sent, $status, $time_gmt,\n$uri, $request_time, $request_length,\n$upstream_status, $upstream_response_time,\n$gzip_ratio,\n$uid_got, $uid_set,\n$connection, $pipe и $msec.\nПараметры в виде \"%name\" скоро будут упразднены.\n</para>\n<para lang=\"en\">\nthe new variables to use in the \"log_format\" directive:\n$bytes_sent, $apache_bytes_sent, $status, $time_gmt,\n$uri, $request_time, $request_length,\n$upstream_status, $upstream_response_time,\n$gzip_ratio,\n$uid_got, $uid_set,\n$connection, $pipe, and $msec.\nThe parameters in the \"%name\" form will be canceled soon.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв директиве \"if\" ложными значениями переменных теперь являются\nпустая строка \"\" и строки, начинающиеся на \"0\".\n</para>\n<para lang=\"en\">\nnow the false variable values in the \"if\" directive are the empty string \"\"\nand string starting with \"0\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри работает с проксированными или FastCGI-серверами nginx мог оставлять\nоткрытыми соединения и временные файлы с запросами клиентов.\n</para>\n<para lang=\"en\">\nwhile using proxied or FastCGI-server nginx may leave connections\nand temporary files with client requests in open state.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nрабочие процессы не сбрасывали буферизированные логи при плавном выходе.\n</para>\n<para lang=\"en\">\nthe worker processes did not flush the buffered logs on graceful exit.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли URI запроса изменялось с помощью rewrite, а затем запрос проксировался\nв location, заданном регулярным выражением, то бэкенду передавался\nневерный запрос;\nошибка появилась в 0.2.6.\n</para>\n<para lang=\"en\">\nif the request URI was changes by the \"rewrite\" directive and the request\nwas proxied in location given by regular expression, then the incorrect\nrequest was transferred to backend;\nthe bug had appeared in 0.2.6.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива expires не удаляла уже установленную строку заголовка \"Expires\".\n</para>\n<para lang=\"en\">\nthe \"expires\" directive did not remove the previous \"Expires\" header.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании метода rtsig и нескольких рабочих процессах nginx\nмог перестать принимать запросы.\n</para>\n<para lang=\"en\">\nnginx may stop to accept requests if the \"rtsig\" method and several worker\nprocesses were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв SSI командах неверно обрабатывались строки \"\\\"\" и \"\\'\".\n</para>\n<para lang=\"en\">\nthe \"\\\"\" and \"\\'\" escape symbols were incorrectly handled in SSI commands.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли ответ заканчивался сразу же после SSI команды, то при использовании\nсжатия ответ передавался не до конца или не передавался вообще.\n</para>\n<para lang=\"en\">\nif the response was ended just after the SSI command and gzipping was used,\nthen the response did not transferred complete or did not transferred at all.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.7\" date=\"2005-10-27\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива access_log поддерживает параметр buffer=.\n</para>\n<para lang=\"en\">\nthe \"access_log\" supports the \"buffer=\" parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на платформах, отличных от i386, amd64, sparc и ppc;\nошибка появилась в 0.3.2.\n</para>\n<para lang=\"en\">\nnginx could not be built on platforms different from i386, amd64, sparc,\nand ppc;\nthe bug had appeared in 0.3.2.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.6\" date=\"2005-10-24\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nIMAP/POP3 прокси теперь не передаёт серверу авторизации пустой логин.\n</para>\n<para lang=\"en\">\nnow the IMAP/POP3 proxy do not send the empty login to authorization server.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива log_format поддерживает переменные в виде $name.\n</para>\n<para lang=\"en\">\nthe \"log_format\" supports the variables in the $name form.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли хотя бы в одном сервере не было описано ни одной директивы listen, то\nnginx не слушал на 80 порту;\nошибка появилась в 0.3.3.\n</para>\n<para lang=\"en\">\nif at least in one server was no the \"listen\" directive, then nginx did not\nlisten on the 80 port;\nthe bug had appeared in 0.3.3.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве proxy_pass отсутствовал URI, то всегда использовался порт 80.\n</para>\n<para lang=\"en\">\nif the URI part is omitted in \"proxy_pass\" directive, the 80 port was\nalways used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.5\" date=\"2005-10-21\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли логин IMAP/POP3 менялся сервером авторизации, то мог произойти\nsegmentation fault;\nошибка появилась в 0.2.2.\n</para>\n<para lang=\"en\">\nthe segmentation fault may occurred if the IMAP/POP3 login was changed\nby authorization server;\nthe bug had appeared in 0.2.2.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\naccept mutex не работал, все соединения обрабатывались одним рабочим процессом;\nошибка появилась в 0.3.3.\n</para>\n<para lang=\"en\">\nthe accept mutex did not work and all connections were handled by one process;\nthe bug had appeared in 0.3.3.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании метода rtsig и директивы timer_resolution\nне работали таймауты.\n</para>\n<para lang=\"en\">\nthe timeout did not work if the \"rtsig\" method and the \"timer_resolution\"\ndirective were used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.4\" date=\"2005-10-19\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на Linux 2.4+ и MacOS X;\nошибка появилась в 0.3.3.\n</para>\n<para lang=\"en\">\nnginx could not be built on Linux 2.4+ and MacOS X;\nthe bug had appeared in 0.3.3.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.3\" date=\"2005-10-19\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметры \"bl\" и \"af\" директивы listen переименованы в \"backlog\"\nи \"accept_filter\".\n</para>\n<para lang=\"en\">\nthe \"bl\" and \"af\" parameters of the \"listen\" directive was renamed to\nthe \"backlog\" and \"accept_filter\".\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры \"rcvbuf\" и \"sndbuf\" в директиве listen.\n</para>\n<para lang=\"en\">\nthe \"rcvbuf\" and \"sndbuf\" parameters of the \"listen\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметр лога $msec теперь не требует дополнительного системного\nвызова gettimeofday().\n</para>\n<para lang=\"en\">\nthe \"$msec\" log parameter does not require now the additional\nthe gettimeofday() system call.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nключ -t теперь проверяет директивы listen.\n</para>\n<para lang=\"en\">\nthe -t switch now tests the \"listen\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в директиве listen был указан неверный адрес, то nginx после\nсигнала -HUP оставлял открытый сокет в состоянии CLOSED.\n</para>\n<para lang=\"en\">\nif the invalid address was specified in the \"listen\" directive, then\nafter the -HUP signal nginx left an open socket in the CLOSED state.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдля индексных файлов, содержащих в имени переменную, мог неверно выставляться\nтип mime по умолчанию;\nошибка появилась в 0.3.0.\n</para>\n<para lang=\"en\">\nthe mime type may be incorrectly set to default value for index file with\nvariable in the name;\nthe bug had appeared in 0.3.0.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива timer_resolution.\n</para>\n<para lang=\"en\">\nthe \"timer_resolution\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр лога $upstream_response_time в миллисекундах.\n</para>\n<para lang=\"en\">\nthe millisecond \"$upstream_response_time\" log parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nвременный файл с телом запроса клиента теперь удаляется сразу после того,\nкак клиенту передан заголовок ответа.\n</para>\n<para lang=\"en\">\na temporary file with client request body now is removed just after\nthe response header was transferred to a client.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с OpenSSL 0.9.6.\n</para>\n<para lang=\"en\">\nOpenSSL 0.9.6 compatibility.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпути к файлам с SSL сертификатом и ключом не могли быть относительными.\n</para>\n<para lang=\"en\">\nthe SSL certificate and key file paths could not be relative.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива ssl_prefer_server_ciphers не работала для модуля ngx_imap_ssl_module.\n</para>\n<para lang=\"en\">\nthe \"ssl_prefer_server_ciphers\" directive did not work in\nthe ngx_imap_ssl_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива ssl_protocols позволяла задать только один протокол.\n</para>\n<para lang=\"en\">\nthe \"ssl_protocols\" directive allowed to specify the single protocol only.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.2\" date=\"2005-10-12\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка Sun Studio 10 C compiler.\n</para>\n<para lang=\"en\">\nthe Sun Studio 10 C compiler support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_upstream_max_fails, proxy_upstream_fail_timeout,\nfastcgi_upstream_max_fails и fastcgi_upstream_fail_timeout.\n</para>\n<para lang=\"en\">\nthe \"proxy_upstream_max_fails\", \"proxy_upstream_fail_timeout\",\n\"fastcgi_upstream_max_fails\", and \"fastcgi_upstream_fail_timeout\"\ndirectives.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.1\" date=\"2005-10-10\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво время переполнения очереди сигналов при использовании метода rtsig\nпроисходил segmentation fault;\nошибка появилась в 0.2.0.\n</para>\n<para lang=\"en\">\nthe segmentation fault occurred when the signal queue overflowed\nif the \"rtsig\" method was used;\nthe bug had appeared in 0.2.0.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nкорректная обработка пар \"\\\\\", \"\\\"\", \"\\'\" и \"\\$\" в SSI.\n</para>\n<para lang=\"en\">\ncorrect handling of the \"\\\\\", \"\\\"\", \"\\'\", and \"\\$\" pairs in SSI.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.3.0\" date=\"2005-10-07\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nубрано десятидневное ограничение времени работы рабочего процесса.\nОграничение было введено из-за переполнения миллисекундных таймеров.\n</para>\n<para lang=\"en\">\nthe 10-days live time limit of worker process was eliminated.\nThe limit was introduced because of millisecond timers overflow.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.2.6\" date=\"2005-10-05\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nс 60 до 10 секунд уменьшено время повторного обращения к бэкенду\nпри использовании распределения нагрузки.\n</para>\n<para lang=\"en\">\nwhile using load-balancing the time before the failed backend retry\nwas decreased from 60 to 10 seconds.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива proxy_pass_unparsed_uri упразднена, оригинальный запрос теперь\nпередаётся, если в директиве proxy_pass отсутствует URI.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass_unparsed_uri\" was canceled, the original URI now passed,\nif the URI part is omitted in \"proxy_pass\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива error_page поддерживает редиректы и позволяет более гибко\nменять код ошибки.\n</para>\n<para lang=\"en\">\nthe \"error_page\" directive supports redirects and allows more flexible\nto change an error code.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв проксированных подзапросах теперь игнорируется переданный charset.\n</para>\n<para lang=\"en\">\nthe charset in the \"Content-Type\" header line now is ignored\nin proxied subrequests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли после изменения URI в блоке if для запроса не находилась\nновая конфигурация, то правила модуля ngx_http_rewrite_module выполнялись\nснова.\n</para>\n<para lang=\"en\">\nif the URI was changed in the \"if\" block and request did not found\nnew configuration, then the ngx_http_rewrite_module rules ran again.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли директива set устанавливала переменную модуля ngx_http_geo_module\nв какой-либо части конфигурации, то эта переменная не была доступна в\nдругих частях конфигурации и выдавалась ошибка \"using uninitialized variable\";\nошибка появилась в 0.2.2.\n</para>\n<para lang=\"en\">\nif the \"set\" directive set the ngx_http_geo_module variable in some\nconfiguration part, the this variable was not available in other\nconfiguration parts and the \"using uninitialized variable\" error was occurred;\nthe bug had appeared in 0.2.2.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.2.5\" date=\"2005-10-04\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдублирующее значение переменной модуля ngx_http_geo_module теперь\nвыдаёт предупреждение и изменяет старое значение.\n</para>\n<para lang=\"en\">\nthe duplicate value of the ngx_http_geo_module variable now causes\nthe warning and changes old value.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает команду set.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports the \"set\" command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает параметр file в команде include.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports the \"file\" parameter in the \"include\" command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает подстановку значений переменных\nв выражениях команды if.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports the variable value substitutions in\nexpressions of the \"if\" command.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.2.4\" date=\"2005-10-03\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает выражения\n\"$var=text\", \"$var!=text\", \"$var=/text/\" и \"$var!=/text/\"\nв команде if.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports\n\"$var=text\", \"$var!=text\", \"$var=/text/\", and \"$var!=/text/\" expressions\nin the \"if\" command.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при проксировании location без слэша в конце;\nошибка появилась в 0.1.44.\n</para>\n<para lang=\"en\">\nin proxying location without trailing slash;\nthe bug had appeared in 0.1.44.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании метода rtsig мог произойти segmentation fault;\nошибка появилась в 0.2.0.\n</para>\n<para lang=\"en\">\nthe segmentation fault may occurred if the \"rtsig\" method was used;\nthe bug had appeared in 0.2.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.2.3\" date=\"2005-09-30\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался без параметра --with-debug;\nошибка появилась в 0.2.2.\n</para>\n<para lang=\"en\">\nnginx could not be built without the --with-debug option;\nthe bug had appeared in 0.2.2.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.2.2\" date=\"2005-09-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nкоманда config errmsg в модуле ngx_http_ssi_module.\n</para>\n<para lang=\"en\">\nthe \"config errmsg\" command of the ngx_http_ssi_module.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпеременные модуля ngx_http_geo_module можно переопределять директивой set.\n</para>\n<para lang=\"en\">\nthe ngx_http_geo_module variables can be overridden by the \"set\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssl_protocols и ssl_prefer_server_ciphers модулей\nngx_http_ssl_module и ngx_imap_ssl_module.\n</para>\n<para lang=\"en\">\nthe \"ssl_protocols\" and \"ssl_prefer_server_ciphers\" directives\nof the ngx_http_ssl_module and ngx_imap_ssl_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка в модуле ngx_http_autoindex_module при показе длинных имён файлов;\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module did not show correctly the long file names;\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module теперь не показывает файлы,\nначинающиеся на точку.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module now do not show the files starting by dot.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли SSL handshake завершался с ошибкой, то это могло привести также\nк закрытию другого соединения.<br/>\nСпасибо Rob Mueller.\n</para>\n<para lang=\"en\">\nif the SSL handshake failed then another connection may be closed too.<br/>\nThanks to Rob Mueller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nэкспортные версии MSIE 5.x не могли соединиться по HTTPS.\n</para>\n<para lang=\"en\">\nthe export versions of MSIE 5.x could not connect via HTTPS.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.2.1\" date=\"2005-09-23\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли все бэкенды, используемые для балансировки нагрузки, оказывались\nв нерабочем состоянии после одной ошибки, то nginx мог зациклится;\nошибка появилась в 0.2.0.\n</para>\n<para lang=\"en\">\nif all backend using in load-balancing failed after one error, then\nnginx may got caught in an endless loop;\nthe bug had appeared in 0.2.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.2.0\" date=\"2005-09-23\">\n\n<change>\n<para lang=\"ru\">\nИзменились имена pid-файлов, используемые во время обновления исполняемого\nфайла. Ручное переименование теперь не нужно.\nСтарый основной процесс добавляет к своему pid-файл суффикс \".oldbin\"\nи запускает новый исполняемый файл.\nНовый основной процесс создаёт обычный pid-файл без суффикса \".newbin\".\nЕсли новый основной процесс выходит, то старый процесс переименовывает свой\npid-файл c суффиксом \".oldbin\" в pid-файл без суффикса.\nПри обновлении с версии 0.1.х до 0.2.0 нужно учитывать, что оба\nпроцесса&mdash;старый 0.1.x и новый 0.2.0&mdash;используют pid-файл\nбез суффиксов.\n</para>\n<para lang=\"en\">\nThe pid-file names used during online upgrade was changed and now is not\nrequired a manual rename operation.\nThe old master process adds the \".oldbin\" suffix to its pid-file and\nexecutes a new binary file.\nThe new master process creates usual pid-file without the \".newbin\" suffix.\nIf the master process exits, then old master process renames back\nits pid-file with the \".oldbin\" suffix to the pid-file without suffix.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива worker_connections, новое название директивы connections;\nдиректива теперь задаёт максимальное число соединений,\nа не максимально возможный номер дескриптора для сокета.\n</para>\n<para lang=\"en\">\nthe \"worker_connections\" directive, new name of the \"connections\" directive;\nnow the directive specifies maximum number of connections,\nbut not maximum socket descriptor number.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nSSL поддерживает кэширование сессий в пределах одного рабочего процесса.\n</para>\n<para lang=\"en\">\nSSL supports the session cache inside one worker process.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива satisfy_any.\n</para>\n<para lang=\"en\">\nthe \"satisfy_any\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nмодули ngx_http_access_module и ngx_http_auth_basic_module не работают\nдля подзапросов.\n</para>\n<para lang=\"en\">\nthe ngx_http_access_module and ngx_http_auth_basic_module do not run\nfor subrequests.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы worker_rlimit_nofile и worker_rlimit_sigpending.\n</para>\n<para lang=\"en\">\nthe \"worker_rlimit_nofile\" and \"worker_rlimit_sigpending\" directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли все бэкенды, используемые для балансировки нагрузки, оказывались\nв нерабочем состоянии после одной ошибки, то nginx не обращался к ним\nв течение 60 секунд.\n</para>\n<para lang=\"en\">\nif all backend using in load-balancing failed after one error, then\nnginx did not try do connect to them during 60 seconds.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв парсинге аргументов IMAP/POP3 команд.<br/>\nСпасибо Rob Mueller.\n</para>\n<para lang=\"en\">\nin IMAP/POP3 command argument parsing.<br/>\nThanks to Rob Mueller.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании SSL в IMAP/POP3 прокси.\n</para>\n<para lang=\"en\">\nerrors while using SSL in IMAP/POP3 proxy.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании SSI и сжатия.\n</para>\n<para lang=\"en\">\nerrors while using SSI and gzipping.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв ответах 304 не добавлялись строки заголовка ответа \"Expires\" и\n\"Cache-Control\".<br/>\nСпасибо Александру Кукушкину.\n</para>\n<para lang=\"en\">\nthe \"Expires\" and \"Cache-Control\" header lines were omitted\nfrom the 304 responses.<br/>\nThanks to Alexandr Kukushkin.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.45\" date=\"2005-09-08\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива ssl_engine упразднена в модуле ngx_http_ssl_module и\nперенесена на глобальный уровень.\n</para>\n<para lang=\"en\">\nthe \"ssl_engine\" directive was canceled in the ngx_http_ssl_module\nand now is introduced at global level.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nответы с подзапросами, включённые с помощью SSI, не передавались\nчерез SSL соединение.\n</para>\n<para lang=\"en\">\nthe responses with SSI subrequests did not transferred via SSL connection.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nРазные исправления в IMAP/POP3 прокси.\n</para>\n<para lang=\"en\">\nVarious bug fixes in the IMAP/POP3 proxy.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.44\" date=\"2005-09-06\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nIMAP/POP3 прокси поддерживает SSL.\n</para>\n<para lang=\"en\">\nthe IMAP/POP3 proxy supports SSL.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_timeout модуля ngx_imap_proxy_module.\n</para>\n<para lang=\"en\">\nthe \"proxy_timeout\" directive of the ngx_imap_proxy_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива userid_mark.\n</para>\n<para lang=\"en\">\nthe \"userid_mark\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nзначение переменной $remote_user определяется независимо от того,\nиспользуется ли авторизация или нет.\n</para>\n<para lang=\"en\">\nthe $remote_user variable value is determined independently of\nauthorization use.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.43\" date=\"2005-08-30\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nlisten(2) backlog в директиве listen можно менять по сигналу -HUP.\n</para>\n<para lang=\"en\">\nthe listen(2) backlog in the \"listen\" directive\ncan be changed using the -HUP signal.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nскрипт geo2nginx.pl добавлен в contrib.\n</para>\n<para lang=\"en\">\nthe geo2nginx.pl script was added to contrib.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпараметры FastCGI с пустым значениями теперь передаются серверу.\n</para>\n<para lang=\"en\">\nthe FastCGI parameters with the empty values now are passed to a server.\n</para>\n</change>\n\n<!--\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри ошибках в работе с проксированным сервером или FastCGI сервером\nмог произойти segmentation fault;\nв режиме прокси ошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nthe segmentation fault may occurred if there were errors while\nworking with proxied or FastCGI server;\nin the proxied mode the bug had appeared in 0.1.29.\n</para>\n</change>\n\n-->\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в ответе проксированного сервера или FastCGI сервера была строка\n\"Cache-Control\", то при использовании директивы expires происходил\nsegmentation fault или рабочий процесс мог зациклится;\nв режиме прокси ошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nthe segmentation fault occurred or the worker process may got caught\nin an endless loop if the proxied or FastCGI server sent the \"Cache-Control\"\nheader line and the \"expires\" directive was used;\nin the proxied mode the bug had appeared in 0.1.29.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.42\" date=\"2005-08-23\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли URI запроса получался нулевой длины после обработки модулем\nngx_http_rewrite_module, то в модуле ngx_http_proxy_module происходил\nsegmentation fault или bus error.\n</para>\n<para lang=\"en\">\nif the request URI had a zero length after the processing in\nthe ngx_http_proxy_module, then the segmentation fault or bus error occurred\nin the ngx_http_proxy_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива limit_rate не работала внутри блока if;\nошибка появилась в 0.1.38.\n</para>\n<para lang=\"en\">\nthe \"limit_rate\" directive did not work inside the \"if\" block;\nthe bug had appeared in 0.1.38.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.41\" date=\"2005-07-25\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли переменная использовалась в файле конфигурации,\nто она не могла использоваться в SSI.\n</para>\n<para lang=\"en\">\nif the variable was used in the configuration file,\nthen it can not be used in SSI.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.40\" date=\"2005-07-22\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли клиент слал очень длинную строку заголовка, то в логе не помещалась\nинформация, связанная с этим запросом.\n</para>\n<para lang=\"en\">\nif a client sent too long header line, then the request information\ndid not logged in the error log.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании \"X-Accel-Redirect\" не передавалась строка \"Set-Cookie\";\nошибка появилась в 0.1.39.\n</para>\n<para lang=\"en\">\nthe \"Set-Cookie\" header line was not transferred when the \"X-Accel-Redirect\"\nwas used;\nthe bug had appeared in 0.1.39.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании \"X-Accel-Redirect\" не передавалась строка\n\"Content-Disposition\".\n</para>\n<para lang=\"en\">\nthe \"Content-Disposition\" header line was not transferred when\nthe \"X-Accel-Redirect\" was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпо сигналу SIGQUIT основной процесс не закрывал сокеты, на которых он слушал.\n</para>\n<para lang=\"en\">\nthe master process did not close the listen socket on the SIGQUIT signal.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле обновления исполняемого файла на лету на Linux и Solaris\nназвание процесса в команде ps становилось короче.\n</para>\n<para lang=\"en\">\nafter on-line upgrade on Linux and Solaris the process name\nbecame shorter in the \"ps\" command.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.39\" date=\"2005-07-14\">\n\n<change>\n<para lang=\"ru\">\nИзменения в модуле ngx_http_charset_module:\nдиректива default_charset упразднена;\nдиректива charset задаёт кодировку ответа;\nдиректива source_charset задаёт только исходную кодировку.\n</para>\n<para lang=\"en\">\nThe changes in the ngx_http_charset_module:\nthe \"default_charset\" directive was canceled;\nthe \"charset\" directive sets the response charset;\nthe \"source_charset\" directive sets the source charset only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении ошибки 401, полученной от бэкенда, не передавалась\nстрока заголовка \"WWW-Authenticate\".\n</para>\n<para lang=\"en\">\nthe backend \"WWW-Authenticate\" header line did not transferred while\nthe 401 response code redirecting.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодули ngx_http_proxy_module и ngx_http_fastcgi_module могли закрыть\nсоединение до того, как что-нибудь было передано клиенту;\nошибка появилась в 0.1.38.\n</para>\n<para lang=\"en\">\nthe ngx_http_proxy_module and ngx_http_fastcgi_module may close\na connection before anything was transferred to a client;\nthe bug had appeared in 0.1.38.\n</para>\n</change>\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nобработка ошибки инициализации в crypt_r() в Linux glibc.\n</para>\n<para lang=\"en\">\nthe Linux glibc crypt_r() initialization bug.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module не поддерживал относительные URI в\nкоманде include virtual.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module did not support the relative URI in\nthe \"include virtual\" command.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в строке заголовка ответа бэкенда была строка \"Location\",\nкоторую nginx не должен был изменять, то в ответе передавалось тело 500 ошибки;\nошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nif the backend response had the \"Location\" header line and nginx\nshould not rewrite this line, then the 500 code response body was transferred;\nthe bug had appeared in 0.1.29.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнекоторые директивы модулей ngx_http_proxy_module и ngx_http_fastcgi_module\nне наследовались с уровня server на уровень location;\nошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nsome directives of the ngx_http_proxy_module and ngx_http_fastcgi_module\nwere not inherited from the server to the location level;\nthe bug had appeared in 0.1.29.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_ssl_module не поддерживал цепочки сертификатов.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssl_module did not support the certificate chain.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка в модуле ngx_http_autoindex_module при показе длинных имён файлов;\nошибка появилась в 0.1.38.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module did not show correctly the long file names;\nthe bug had appeared in 0.1.38.\n</para>\n</change>\n\n<change>\n<para lang=\"ru\">\nИсправления в IMAP/POP3 прокси при взаимодействии с бэкендом на стадии login.\n</para>\n<para lang=\"en\">\nBugfixes in IMAP/POP3 proxy in interaction with a backend at the login state.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.38\" date=\"2005-07-08\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива limit_rate поддерживается в режиме прокси и FastCGI.\n</para>\n<para lang=\"en\">\nthe \"limit_rate\" directive is supported in proxy and FastCGI mode.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв режиме прокси и FastCGI поддерживается строка заголовка \"X-Accel-Limit-Rate\"\nв ответе бэкенда.\n</para>\n<para lang=\"en\">\nthe \"X-Accel-Limit-Rate\" response header line is supported in proxy\nand FastCGI mode.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива break.\n</para>\n<para lang=\"en\">\nthe \"break\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива log_not_found.\n</para>\n<para lang=\"en\">\nthe \"log_not_found\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри перенаправлении запроса с помощью строки заголовка \"X-Accel-Redirect\"\nне изменялся код ответа.\n</para>\n<para lang=\"en\">\nthe response status code was not changed when request was redirected\nby the \"\"X-Accel-Redirect\" header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременные, установленные директивой set не могли использоваться в SSI.\n</para>\n<para lang=\"en\">\nthe variables set by the \"set\" directive could not be used in SSI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включении в SSI более одного удалённого подзапроса\nмог произойти segmentation fault.\n</para>\n<para lang=\"en\">\nthe segmentation fault may occurred if the SSI page has more than one\nremote subrequest.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли статусная строка в ответе бэкенда передавалась в двух пакетах, то\nnginx считал ответ неверным;\nошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nnginx treated the backend response as invalid if the status line in the\nheader was transferred in two packets;\nthe bug had appeared in 0.1.29.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssi_types.\n</para>\n<para lang=\"en\">\nthe \"ssi_types\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива autoindex_exact_size.\n</para>\n<para lang=\"en\">\nthe \"autoindex_exact_size\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module не поддерживал длинные имена файлов в UTF-8.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module did not support the long file names in UTF-8.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nIMAP/POP3 прокси.\n</para>\n<para lang=\"en\">\nthe IMAP/POP3 proxy.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.37\" date=\"2005-06-23\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nв конце файла nginx.pid теперь добавляется \"\\n\".\n</para>\n<para lang=\"en\">\nnow the \"\\n\" is added to the end of the \"nginx.pid\" file.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включении большого количества вставок или нескольких больших вставок\nс помощью SSI ответ мог передаваться не полностью.\n</para>\n<para lang=\"en\">\nthe responses may be transferred not completely,\nif many parts or the big parts were included by SSI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли все бэкенды возвращали ответ 404, то при использовании параметра http_404\nв директивах proxy_next_upstream или fastcgi_next_upstream, nginx\nначинал запрашивать все бэкенды снова.\n</para>\n<para lang=\"en\">\nif all backends had returned the 404 response and the \"http_404\" parameter of\nthe \"proxy_next_upstream\" or \"fastcgi_next_upstream\" directives was used,\nthen nginx started to request all backends again.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.36\" date=\"2005-06-15\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nесли в заголовке запроса есть дублирующиеся строки \"Host\", \"Connection\",\n\"Content-Length\" и \"Authorization\", то nginx теперь выдаёт ошибку 400.\n</para>\n<para lang=\"en\">\nif the request header has duplicate the \"Host\", \"Connection\", \"Content-Length\",\nor \"Authorization\" lines, then nginx now returns the 400 error.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива post_accept_timeout упразднена.\n</para>\n<para lang=\"en\">\nthe \"post_accept_timeout\" directive was canceled.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры default, af=, bl=, deferred и bind в директиве listen.\n</para>\n<para lang=\"en\">\nthe \"default\", \"af=\", \"bl=\", \"deferred\", and \"bind\" parameters\nof the \"listen\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка accept фильтров во FreeBSD.\n</para>\n<para lang=\"en\">\nthe FreeBSD accept filters support.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка TCP_DEFER_ACCEPT в Linux.\n</para>\n<para lang=\"en\">\nthe Linux TCP_DEFER_ACCEPT support.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module не поддерживал имена файлов в UTF-8.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module did not support the file names in UTF-8.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпосле добавления новый лог-файл ротация этого лога по сигналу -USR1\nвыполнялась, только если переконфигурировать nginx два раза по сигналу -HUP.\n</para>\n<para lang=\"en\">\nthe new log file can be rotated by the -USR1 signal only if\nthe reconfiguration by the -HUP signal was made twice.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.35\" date=\"2005-06-07\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива working_directory.\n</para>\n<para lang=\"en\">\nthe \"working_directory\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива port_in_redirect.\n</para>\n<para lang=\"en\">\nthe \"port_in_redirect\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли заголовок ответа бэкенда не помещался в один пакет, то\nпроисходил segmentation fault;\nошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nthe segmentation fault was occurred if the backend response header was in\nseveral packets;\nthe bug had appeared in 0.1.29.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли было сконфигурировано более 10 серверов или в сервере не описана\nдиректива \"listen\",\nто при запуске мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\nif more than 10 servers were configured or some server did not use the\n\"listen\" directive, then the segmentation fault was occurred on the start.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли ответ не помещался во временный файл,\nто мог произойти segmentation fault.\n</para>\n<para lang=\"en\">\nthe segmentation fault might occur if the response was bigger than\nthe temporary file.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx возвращал ошибку 400 на запросы вида\n<nobr>\"GET http://www.domain.com/uri HTTP/1.0\"</nobr>;\nошибка появилась в 0.1.28.\n</para>\n<para lang=\"en\">\nnginx returned the 400 response on requests like\n<nobr>\"GET http://www.domain.com/uri HTTP/1.0\"</nobr>;\nthe bug had appeared in 0.1.28.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.34\" date=\"2005-05-26\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри включении больших ответов с помощью SSI рабочий процесс мог зациклиться.\n</para>\n<para lang=\"en\">\nthe worker process may got caught in an endless loop if the big response\npart were include by SSI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпеременные, устанавливаемые директивой \"set\", не были доступны в SSI.\n</para>\n<para lang=\"en\">\nthe variables set by the \"set\" directive were not available in SSI.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива autoindex_localtime.\n</para>\n<para lang=\"en\">\nthe \"autoindex_localtime\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпустое значение в директиве proxy_set_header запрещает передачу заголовка.\n</para>\n<para lang=\"en\">\nthe empty value of the \"proxy_set_header\" directive forbids the client\nrequest header line passing.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.33\" date=\"2005-05-23\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался с параметром --without-pcre;\nошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nnginx could not be built with the --without-pcre parameter;\nthe bug had appeared in 0.1.29.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\n3, 5, 7 и 8 директив proxy_set_header на одном уровне вызывали\nbus fault при запуске.\n</para>\n<para lang=\"en\">\n3, 4, 7, and 8 the \"proxy_set_header\" directives in one level cause\nthe bus fault on start up.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв редиректах внутри HTTPS сервера был указан протокол HTTP.\n</para>\n<para lang=\"en\">\nthe HTTP protocol was specified in the HTTPS redirects.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли директива rewrite использовала выделения внутри директивы if, то\nвозвращалась ошибка 500.\n</para>\n<para lang=\"en\">\nif the \"rewrite\" directive used the captures inside the \"if\" directive, then\nthe 500 error code was returned.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.32\" date=\"2005-05-19\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв редиректах, выдаваемых с помощью директивы rewrite, не передавались аргументы;\nошибка появилась в 0.1.29.\n</para>\n<para lang=\"en\">\nthe arguments were omitted in the redirects, issued by the \"rewrite\" directive;\nthe bug had appeared in 0.1.29.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива if поддерживает выделения в регулярных выражениях.\n</para>\n<para lang=\"en\">\nthe \"if\" directive supports the captures in regular expressions.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива set поддерживает переменные и выделения из регулярных выражений.\n</para>\n<para lang=\"en\">\nthe \"set\" directive supports the variables and the captures of regular\nexpressions.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв режиме прокси и FastCGI поддерживается строка заголовка \"X-Accel-Redirect\"\nв ответе бэкенда.\n</para>\n<para lang=\"en\">\nthe \"X-Accel-Redirect\" response header line is supported in proxy and FastCGI\nmode.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.31\" date=\"2005-05-16\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SSL ответ мог передаваться не до конца.\n</para>\n<para lang=\"en\">\nthe response encrypted by SSL may not transferred complete.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при обработке SSI в ответе, полученного от FastCGI-сервера.\n</para>\n<para lang=\"en\">\nerrors while processing FastCGI response by SSI.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки при использовании SSI и сжатия.\n</para>\n<para lang=\"en\">\nerrors while using SSI and gzipping.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nредирект с кодом 301 передавался без тела ответа;\nошибка появилась в 0.1.30.\n</para>\n<para lang=\"en\">\nthe redirect with the 301 code was transferred without response body;\nthe bug had appeared in 0.1.30.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.30\" date=\"2005-05-14\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SSI рабочий процесс мог зациклиться.\n</para>\n<para lang=\"en\">\nthe worker process may got caught in an endless loop if the SSI was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SSL ответ мог передаваться не до конца.\n</para>\n<para lang=\"en\">\nthe response encrypted by SSL may not transferred complete.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли длина части ответа, полученного за один раз от проксируемого или\nFastCGI сервера была равна 500 байт, то nginx возвращал код ответа 500;\nв режиме прокси ошибка появилась только в 0.1.29.\n</para>\n<para lang=\"en\">\nif the length of the response part received at once from proxied\nor FastCGI server was equal to 500, then nginx returns the 500 response code;\nin proxy mode the bug had appeared in 0.1.29 only.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не считал неверными директивы с 8-ю или 9-ю параметрами.\n</para>\n<para lang=\"en\">\nnginx did not consider the directives with 8 or 9 parameters as invalid.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива return может возвращать код ответа 204.\n</para>\n<para lang=\"en\">\nthe \"return\" directive can return the 204 response code.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ignore_invalid_headers.\n</para>\n<para lang=\"en\">\nthe \"ignore_invalid_headers\" directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.29\" date=\"2005-05-12\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает команду include virtual.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports \"include virtual\" command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает условную команду вида\n'if expr=\"$NAME\"' и команды else и endif.\nДопускается только один уровень вложенности.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports the condition command like\n'if expr=\"$NAME\"' and \"else\" and \"endif\" commands.\nOnly one nested level is supported.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_module поддерживает две переменные DATE_LOCAL и DATE_GMT\nи команду config timefmt.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_module supports the DATE_LOCAL and DATE_GMT variables\nand \"config timefmt\" command.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива ssi_ignore_recycled_buffers.\n</para>\n<para lang=\"en\">\nthe \"ssi_ignore_recycled_buffers\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли переменная QUERY_STRING не была определена, то в команде echo\nне ставилось значение по умолчанию.\n</para>\n<para lang=\"en\">\nthe \"echo\" command did not show the default value for the empty QUERY_STRING\nvariable.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nмодуль ngx_http_proxy_module полностью переписан.\n</para>\n<para lang=\"en\">\nthe ngx_http_proxy_module was rewritten.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_redirect, proxy_pass_request_headers,\nproxy_pass_request_body и proxy_method.\n</para>\n<para lang=\"en\">\nthe \"proxy_redirect\", \"proxy_pass_request_headers\",\n\"proxy_pass_request_body\", and \"proxy_method\" directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_set_header.\nДиректива proxy_x_var упразднена и должна быть заменена директивой\nproxy_set_header.\n</para>\n<para lang=\"en\">\nthe \"proxy_set_header\" directive.\nThe \"proxy_x_var\" was canceled and must be replaced with the proxy_set_header\ndirective.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива proxy_preserve_host упразднена и должна быть заменена директивами\n\"proxy_set_header Host $host\" и \"proxy_redirect off\"\nили директивой <nobr>\"proxy_set_header Host $host:$proxy_port\"</nobr>\nи соответствующими ей директивами proxy_redirect.\n</para>\n<para lang=\"en\">\nthe \"proxy_preserve_host\" is canceled and must be replaced with\nthe \"proxy_set_header Host $host\" and the \"proxy_redirect off\" directives,\nthe <nobr>\"proxy_set_header Host $host:$proxy_port\" directive</nobr>\nand the appropriate proxy_redirect directives.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива proxy_set_x_real_ip упразднена и должна быть заменена директивой\n\"proxy_set_header X-Real-IP $remote_addr\".\n</para>\n<para lang=\"en\">\nthe \"proxy_set_x_real_ip\" is canceled and must be replaced with\nthe \"proxy_set_header X-Real-IP $remote_addr\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива proxy_add_x_forwarded_for упразднена и должна быть заменена\nдирективой\n<nobr>\"proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for\".</nobr>\n</para>\n<para lang=\"en\">\nthe \"proxy_add_x_forwarded_for\" is canceled and must be replaced with\n<nobr>the \"proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for\"</nobr>\ndirective.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдиректива proxy_set_x_url упразднена и должна быть заменена директивой\n<nobr>\"proxy_set_header X-URL http://$host:$server_port$request_uri\".</nobr>\n</para>\n<para lang=\"en\">\nthe \"proxy_set_x_url\" is canceled and must be replaced with\nthe \"proxy_set_header X-URL http://$host:$server_port$request_uri\"\ndirective.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_param.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_param\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nдирективы fastcgi_root, fastcgi_set_var и fastcgi_params упразднены\nи должны быть замены директивами fastcgi_param.\n</para>\n<para lang=\"en\">\nthe \"fastcgi_root\", \"fastcgi_set_var\" and \"fastcgi_params\" directive\nare canceled and must be replaced with the fastcgi_param directives.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива index может использовать переменные.\n</para>\n<para lang=\"en\">\nthe \"index\" directive can use the variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива index может быть указана на уровне http и server.\n</para>\n<para lang=\"en\">\nthe \"index\" directive can be used at http and server levels.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nтолько последний параметр в директиве index может быть абсолютным.\n</para>\n<para lang=\"en\">\nthe last index only in the \"index\" directive can be absolute.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nв директиве rewrite могут использоваться переменные.\n</para>\n<para lang=\"en\">\nthe \"rewrite\" directive can use the variables.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива internal.\n</para>\n<para lang=\"en\">\nthe \"internal\" directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпеременные CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR,\nSERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME,\nREQUEST_METHOD, REQUEST_URI и REMOTE_USER.\n</para>\n<para lang=\"en\">\nthe CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR,\nSERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME,\nREQUEST_METHOD, REQUEST_URI, and REMOTE_USER variables.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nnginx теперь передаёт неверные строки в заголовках запроса клиента и\nответа бэкенда.\n</para>\n<para lang=\"en\">\nnginx now passes the invalid lines in a client request headers\nor a backend response header.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли бэкенд долго не передавал ответ и send_timeout был меньше, чем\nproxy_read_timeout, то клиенту возвращался ответ 408.\n</para>\n<para lang=\"en\">\nif the backend did not transfer response for a long time and\nthe \"send_timeout\" was less than \"proxy_read_timeout\", then nginx\nreturned the 408 response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли бэкенд передавал неверную строку в заголовке ответа, то происходил\nsegmentation fault;\nошибка появилась в 0.1.26.\n</para>\n<para lang=\"en\">\nthe segmentation fault was occurred if the backend sent an invalid line\nin response header;\nthe bug had appeared in 0.1.26.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании отказоустойчивой конфигурации в FastCGI мог\nпроисходить segmentation fault.\n</para>\n<para lang=\"en\">\nthe segmentation fault may occurred in FastCGI fault tolerance configuration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива expires не удаляла уже установленные строки заголовка\n\"Expires\" и \"Cache-Control\".\n</para>\n<para lang=\"en\">\nthe \"expires\" directive did not remove the previous \"Expires\" and\n\"Cache-Control\" headers.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не учитывал завершающую точку в строке заголовка запроса \"Host\".\n</para>\n<para lang=\"en\">\nnginx did not take into account trailing dot in \"Host\" header line.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_auth_module не работал на Linux.\n</para>\n<para lang=\"en\">\nthe ngx_http_auth_module did not work under Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива rewrite неверно работала, если в запросе присутствовали аргументы.\n</para>\n<para lang=\"en\">\nthe rewrite directive worked incorrectly, if the arguments were in a request.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на MacOS X.\n</para>\n<para lang=\"en\">\nnginx could not be built on MacOS X.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.28\" date=\"2005-04-08\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании больших файлов nginx сильно нагружал процессор.\n</para>\n<para lang=\"en\">\nnginx hogs CPU while proxying the huge files.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался gcc 4.0 на Linux.\n</para>\n<para lang=\"en\">\nnginx could not be built by gcc 4.0 on Linux.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.27\" date=\"2005-03-28\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр blocked в директиве valid_referers.\n</para>\n<para lang=\"en\">\nthe \"blocked\" parameter of the \"valid_referers\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nошибки обработки заголовка запроса теперь записываются на уровне\ninfo, в лог также записывается имя сервера и строки заголовка\nзапроса \"Host\" и \"Referer\".\n</para>\n<para lang=\"en\">\nthe errors while handling the request header now logged at \"info\" level.\nThe server name and the \"Host\" and \"Referer\" header lines also logged.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпри записи ошибок в лог записывается также строка заголовка запроса \"Host\".\n</para>\n<para lang=\"en\">\nthe \"Host\" header line is also logged in error log.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_pass_unparsed_uri.\nСпециальная обработка символов \"://\" в URI, введённая в версии 0.1.11,\nтеперь упразднена.\n</para>\n<para lang=\"en\">\nthe proxy_pass_unparsed_uri directive.\nThe special handling of the \"://\" symbols in URI, appeared in 0.1.11 version,\nnow is canceled.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на FreeBSD и Linux, если был указан параметр конфигурации\n--without-ngx_http_auth_basic_module.\n</para>\n<para lang=\"en\">\nnginx could not be built on FreeBSD and Linux, if the\n--without-ngx_http_auth_basic_module configuration parameter was used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.26\" date=\"2005-03-22\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nневерные строки заголовка, переданные клиентом, теперь игнорируется и\nзаписываются в error_log на уровне info.\n</para>\n<para lang=\"en\">\nthe invalid client header lines are now ignored and logged at the info level.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпри записи ошибок в лог записывается также имя сервера, при обращении\nк которому произошла ошибка.\n</para>\n<para lang=\"en\">\nthe server name is also logged in error log.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_auth_basic_module и директивы auth_basic и\nauth_basic_user_file.\n</para>\n<para lang=\"en\">\nthe ngx_http_auth_basic_module module and the auth_basic and\nauth_basic_user_file directives.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.25\" date=\"2005-03-19\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не работал на Linux parisc.\n</para>\n<para lang=\"en\">\nnginx did run on Linux parisc.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nnginx теперь не запускается под FreeBSD, если значение\nsysctl kern.ipc.somaxconn слишком большое.\n</para>\n<para lang=\"en\">\nnginx now does not start under FreeBSD if the sysctl kern.ipc.somaxconn\nvalue is too big.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли модуль ngx_http_index_module делал внутреннее перенаправление запроса\nв модули ngx_http_proxy_module или ngx_http_fastcgi_module, то файл индекса\nне закрывался после обслуживания запроса.\n</para>\n<para lang=\"en\">\nif a request was internally redirected by the ngx_http_index_module\nmodule to the ngx_http_proxy_module or ngx_http_fastcgi_module modules,\nthen the index file was not closed after request completion.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_pass может использоваться в location, заданных регулярным\nвыражением.\n</para>\n<para lang=\"en\">\nthe \"proxy_pass\" can be used in location with regular expression.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_rewrite_filter_module поддерживает условия вида\n\"if ($HTTP_USER_AGENT ~ MSIE)\".\n</para>\n<para lang=\"en\">\nthe ngx_http_rewrite_filter_module module supports the condition like\n\"if ($HTTP_USER_AGENT ~ MSIE)\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx очень медленно запускался при большом количестве адресов и\nиспользовании текстовых значений в директиве geo.\n</para>\n<para lang=\"en\">\nnginx started too slow if the large number of addresses and text values\nwere used in the \"geo\" directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nимя переменной в директиве geo нужно указывать, как $name.\nПрежний вариант без \"$\" пока работает, но вскоре будет убран.\n</para>\n<para lang=\"en\">\na variable name must be declared as \"$name\" in the \"geo\" directive.\nThe previous variant without \"$\" is still supported, but will be removed soon.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр лога \"%{VARIABLE}v\".\n</para>\n<para lang=\"en\">\nthe \"%{VARIABLE}v\" logging parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива \"set $name value\".\n</para>\n<para lang=\"en\">\nthe \"set $name value\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nсовместимость с gcc 4.0.\n</para>\n<para lang=\"en\">\ngcc 4.0 compatibility.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр автоконфигурации --with-openssl-opt=OPTIONS.\n</para>\n<para lang=\"en\">\nthe --with-openssl-opt=OPTIONS autoconfiguration directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.24\" date=\"2005-03-04\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_filter_module поддерживает переменные\nQUERY_STRING и DOCUMENT_URI.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_filter_module supports the QUERY_STRING and DOCUMENT_URI\nvariables.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module мог выдавать ответ 404\nна существующий каталог, если этот каталог был указан как alias.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module may some times return the 404 response\nfor existent directory, if this directory was used in \"alias\" directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_filter_module неправильно работал при больших\nответах.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_filter_module ran incorrectly for large responses.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nотсутствие строки заголовка \"Referer\" всегда считалось правильным referrer'ом.\n</para>\n<para lang=\"en\">\nthe lack of the \"Referer\" header line was always accounted as valid referrer.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.23\" date=\"2005-03-01\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_ssi_filter_module и\nдирективы ssi, ssi_silent_errors и ssi_min_file_chunk.\nПоддерживаются команды 'echo var=\"HTTP_...\" default=\"\"' и\n'echo var=\"REMOTE_ADDR\"'.\n</para>\n<para lang=\"en\">\nthe ngx_http_ssi_filter_module and\nthe ssi, ssi_silent_errors, and ssi_min_file_chunk directives.\nThe 'echo var=\"HTTP_...\" default=\"\"' and 'echo var=\"REMOTE_ADDR\"' commands\nare supported.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр лога %request_time.\n</para>\n<para lang=\"en\">\nthe %request_time log parameter.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nесли запрос пришёл без строки заголовка \"Host\", то директива\nproxy_preserve_host устанавливает в качестве этого заголовка первое имя\nсервера из директивы server_name.\n</para>\n<para lang=\"en\">\nif the request has no the \"Host\" header line, then the \"proxy_preserve_host\"\ndirective set this header line to the first server name of the \"server_name\"\ndirective.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался на платформах, отличных от i386, amd64, sparc и ppc;\nошибка появилась в 0.1.22.\n</para>\n<para lang=\"en\">\nnginx could not be built on platforms different from i386, amd64, sparc,\nand ppc;\nthe bug had appeared in 0.1.22.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module теперь показывает информацию не о\nсимволическом линке, а о файле или каталоге, на который он указывает.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module now shows the information not about the symlink,\nbut about file or directory it points to.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли клиенту ничего не передавалось, то параметр %apache_length\nзаписывал в лог отрицательную длину заголовка ответа.\n</para>\n<para lang=\"en\">\nthe %apache_length parameter logged the negative length\nof the response header if the no response was transferred to a client.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.22\" date=\"2005-02-22\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_stub_status_module показывал неверную статистику\nдля обработанных соединений, если использовалось проксирование\nили FastCGI-сервер.\n</para>\n<para lang=\"en\">\nthe ngx_http_stub_status_module showed incorrect handled connections\nstatistics if the proxying or FastCGI server were used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна Linux и Solaris установочные пути были неверно заключены в кавычки;\nошибка появилась в 0.1.21.\n</para>\n<para lang=\"en\">\nthe installation paths were incorrectly quoted on Linux and Solaris;\nthe bug had appeared in 0.1.21.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.21\" date=\"2005-02-22\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль ngx_http_stub_status_module показывал неверную статистику\nпри использовании метода rtsig или при использовании нескольких\nрабочих процессов на SMP машине.\n</para>\n<para lang=\"en\">\nthe ngx_http_stub_status_module showed incorrect statistics\nif \"rtsig\" method was used or if several worker process ran on SMP.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался компилятором icc под Линуксом или\nесли библиотека zlib-1.2.x собиралась из исходных текстов.\n</para>\n<para lang=\"en\">\nnginx could not be built by the icc compiler on Linux or\nif the zlib-1.2.x library was building from sources.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под NetBSD 2.0.\n</para>\n<para lang=\"en\">\nnginx could not be built on NetBSD 2.0.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.20\" date=\"2005-02-17\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновые параметры script_filename и remote_port в директиве fastcgi_params.\n</para>\n<para lang=\"en\">\nthe new \"script_filename\" and \"remote_port\" parameters\nof the fastcgi_params directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнеправильно обрабатывался поток stderr от FastCGI-сервера.\n</para>\n<para lang=\"en\">\nthe FastCGI stderr stream was handled incorrectly.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.19\" date=\"2005-02-16\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в запросе есть нуль, то для локальных запросов теперь возвращается\nошибка 404.\n</para>\n<para lang=\"en\">\nnow, if request contains the zero, then the 404 error is returned\nfor the local requests.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под NetBSD 2.0.\n</para>\n<para lang=\"en\">\nnginx could not be built on NetBSD 2.0.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nво время чтения тела запроса клиента в SSL соединении мог произойти таймаут.\n</para>\n<para lang=\"en\">\nthe timeout may occur while reading of the client request body\nvia SSL connections.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.18\" date=\"2005-02-09\">\n\n<change type=\"workaround\">\n<para lang=\"ru\">\nдля совместимости с Solaris 10 в директивах devpoll_events и devpoll_changes\nзначения по умолчанию уменьшены с 512 до 32.\n</para>\n<para lang=\"en\">\nthe default values of the devpoll_events and the devpoll_changes directives\nchanged from 512 to 32 to be compatible with Solaris 10.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдирективы proxy_set_x_var и fastcgi_set_var не наследовались.\n</para>\n<para lang=\"en\">\nthe proxy_set_x_var and fastcgi_set_var directives were not inherited.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв директиве rewrite, возвращающей редирект, аргументы присоединялись\nк URI через символ \"&amp;\" вместо \"?\".\n</para>\n<para lang=\"en\">\nin a redirect rewrite directive arguments were concatenated with URI\nby an \"&amp;\" rather than a \"?\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nстроки для модуля ngx_http_geo_module без символа \";\" во включённом файле\nигнорировались.\n</para>\n<para lang=\"en\">\nthe lines without trailing \";\" in the file being included\nby the ngx_http_geo_module were silently ignored.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_stub_status_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_stub_status_module.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнеизвестный формат лог-файла в директиве access_log вызывал segmentation fault.\n</para>\n<para lang=\"en\">\nthe unknown log format in the access_log directive caused\nthe segmentation fault.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновый параметр document_root в директиве fastcgi_params.\n</para>\n<para lang=\"en\">\nthe new \"document_root\" parameter of the fastcgi_params directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива fastcgi_redirect_errors.\n</para>\n<para lang=\"en\">\nthe fastcgi_redirect_errors directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nновый модификатор break в директиве rewrite позволяет прекратить\nцикл rewrite/location и устанавливает текущую конфигурацию для запроса.\n</para>\n<para lang=\"en\">\nthe new \"break\" modifier of the \"rewrite\" directive allows to stop\nthe rewrite/location cycle and sets the current configuration to the request.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.17\" date=\"2005-02-03\">\n\n<change type=\"change\">\n<para lang=\"ru\">\nмодуль ngx_http_rewrite_module полностью переписан.\nТеперь можно делать редиректы, возвращать коды ошибок\nи проверять переменные и рефереры.\nЭти директивы можно использовать внутри location.\nДиректива redirect упразднена.\n</para>\n<para lang=\"en\">\nthe ngx_http_rewrite_module was rewritten from the scratch.\nNow it is possible to redirect, to return the error codes,\nto check the variables and referrers. The directives can be used\ninside locations.\nThe redirect directive was canceled.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_geo_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_geo_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы proxy_set_x_var и fastcgi_set_var.\n</para>\n<para lang=\"en\">\nthe proxy_set_x_var and fastcgi_set_var directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nконфигурация location с модификатором \"=\" могла использоваться\nв другом location.\n</para>\n<para lang=\"en\">\nthe location configuration with \"=\" modifier may be used in another\nlocation.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nправильный тип ответа выставлялся только для запросов, у которых в расширении\nбыли только маленькие буквы.\n</para>\n<para lang=\"en\">\nthe correct content type was set only for requests that use small caps letters\nin extension.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли для location установлен proxy_pass или fastcgi_pass, и доступ\nк нему запрещался, а ошибка перенаправлялась на статическую страницу,\nто происходил segmentation fault.\n</para>\n<para lang=\"en\">\nif the proxy_pass or fastcgi_pass directives were set in the location,\nand access was denied, and the error was redirected to a static page,\nthen the segmentation fault occurred.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в проксированном ответе в заголовке \"Location\" передавался\nотносительный URL, то к нему добавлялось имя хоста и слэш;\nошибка появилась в 0.1.14.\n</para>\n<para lang=\"en\">\nif in a proxied \"Location\" header was a relative URL,\nthen a host name and a slash were added to them;\nthe bug had appeared in 0.1.14.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна Linux в лог не записывался текст системной ошибки.\n</para>\n<para lang=\"en\">\nthe system error message was not logged on Linux.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.16\" date=\"2005-01-25\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли ответ передавался chunk'ами, то при запросе HEAD выдавался\nзавершающий chunk.\n</para>\n<para lang=\"en\">\nif the response were transferred by chunks, then on the HEAD request\nthe final chunk was issued.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nзаголовок \"Connection: keep-alive\" выдавался, даже если директива\nkeepalive_timeout запрещала использование keep-alive.\n</para>\n<para lang=\"en\">\nthe \"Connection: keep-alive\" header were issued, even if the\nkeepalive_timeout directive forbade the keep-alive use.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки в модуле ngx_http_fastcgi_module вызывали segmentation fault.\n</para>\n<para lang=\"en\">\nthe errors in the ngx_http_fastcgi_module caused the segmentation faults.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании SSL сжатый ответ мог передаваться не до конца.\n</para>\n<para lang=\"en\">\nthe compressed response encrypted by SSL may not transferred complete.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nопции TCP_NODELAY, TCP_NOPUSH и TCP_CORK, специфичные для TCP сокетов,\nне используются для unix domain сокетов.\n</para>\n<para lang=\"en\">\nthe TCP-specific TCP_NODELAY, TCP_NOPUSH, and TCP_CORK options,\nare not used for the unix domain sockets.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива rewrite поддерживает перезаписывание аргументов.\n</para>\n<para lang=\"en\">\nthe rewrite directive supports the arguments rewriting.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна запрос POST с заголовком \"Content-Length: 0\" возвращался ответ 400;\nошибка появилась в 0.1.14.\n</para>\n<para lang=\"en\">\nthe response code 400 was returned for the POST request with the\n\"Content-Length: 0\" header;\nthe bug had appeared in 0.1.14.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.15\" date=\"2005-01-19\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка соединения с FastCGI-сервером вызывала segmentation fault.\n</para>\n<para lang=\"en\">\nthe error while the connecting to the FastCGI server caused\nsegmentation fault.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nкорректная обработка регулярного выражения, в котором число\nвыделенных частей не совпадает с числом подстановок.\n</para>\n<para lang=\"en\">\nthe correct handling of the regular expression, that\nhas different number of the captures and substitutions.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nlocation, который передаётся FastCGI-серверу, может быть задан\nс помощью регулярного выражения.\n</para>\n<para lang=\"en\">\nthe location, that is passed to the FastCGI server, can be\nregular expression.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр FastCGI REQUEST_URI теперь передаётся вместе с аргументами\nи в том виде, в котором был получен от клиента.\n</para>\n<para lang=\"en\">\nthe FastCGI's parameter REQUEST_URI is now passed with the arguments\nand in the original state.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдля использования регулярных выражений в location нужно было\nсобирать nginx вместе с ngx_http_rewrite_module.\n</para>\n<para lang=\"en\">\nthe ngx_http_rewrite_module module was required to be built to use\nthe regular expressions in locations.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли бэкенд слушал на 80-ом порту, то при использовании директивы\n<nobr>\"proxy_preserve_host  on\"</nobr> в заголовке \"Host\" указывался\nтакже порт 80;\nошибка появилась в 0.1.14.\n</para>\n<para lang=\"en\">\nthe directive <nobr>\"proxy_preserve_host  on\"</nobr> adds port 80\nto the \"Host\" headers, if upstream listen on port 80;\nthe bug had appeared in 0.1.14.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли задать одинаковые пути в параметрах автоконфигурации\n--http-client-body-temp-path=PATH и --http-proxy-temp-path=PATH\nили --http-client-body-temp-path=PATH и --http-fastcgi-temp-path=PATH,\nто происходил segmentation fault.\n</para>\n<para lang=\"en\">\nthe same paths in autoconfiguration parameters\n--http-client-body-temp-path=PATH and --http-proxy-temp-path=PATH,\nor --http-client-body-temp-path=PATH and --http-fastcgi-temp-path=PATH\ncaused segmentation fault.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.14\" date=\"2005-01-18\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры автоконфигурации\n--http-client-body-temp-path=PATH,\n--http-proxy-temp-path=PATH\nи --http-fastcgi-temp-path=PATH\n</para>\n<para lang=\"en\">\nthe autoconfiguration directives:\n--http-client-body-temp-path=PATH,\n--http-proxy-temp-path=PATH,\nand --http-fastcgi-temp-path=PATH\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nимя каталога с временными файлами, содержащие тело запроса клиента,\nзадаётся директивой client_body_temp_path,\nпо умолчанию &lt;prefix&gt;/client_body_temp.\n</para>\n<para lang=\"en\">\nthe directory name for the temporary files with the client request body\nis specified by directive client_body_temp_path,\nby default it is &lt;prefix&gt;/client_body_temp.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_fastcgi_module и директивы\nfastcgi_pass,\nfastcgi_root,\nfastcgi_index,\nfastcgi_params,\nfastcgi_connect_timeout,\nfastcgi_send_timeout,\nfastcgi_read_timeout,\nfastcgi_send_lowat,\nfastcgi_header_buffer_size,\nfastcgi_buffers,\nfastcgi_busy_buffers_size,\nfastcgi_temp_path,\nfastcgi_max_temp_file_size,\nfastcgi_temp_file_write_size,\nfastcgi_next_upstream\nи fastcgi_x_powered_by.\n\n</para>\n<para lang=\"en\">\nthe ngx_http_fastcgi_module and the directives:\nfastcgi_pass,\nfastcgi_root,\nfastcgi_index,\nfastcgi_params,\nfastcgi_connect_timeout,\nfastcgi_send_timeout,\nfastcgi_read_timeout,\nfastcgi_send_lowat,\nfastcgi_header_buffer_size,\nfastcgi_buffers,\nfastcgi_busy_buffers_size,\nfastcgi_temp_path,\nfastcgi_max_temp_file_size,\nfastcgi_temp_file_write_size,\nfastcgi_next_upstream,\nand fastcgi_x_powered_by.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка \"[alert] zero size buf\";\nошибка появилась в 0.1.3.\n</para>\n<para lang=\"en\">\nthe \"[alert] zero size buf\" error;\nthe bug had appeared in 0.1.3.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nв директиве proxy_pass нужно обязательно указывать URI после имени хоста.\n</para>\n<para lang=\"en\">\nthe URI must be specified after the host name in the proxy_pass directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nесли в URI встречался символ %3F, то он считался началом строки аргументов.\n</para>\n<para lang=\"en\">\nthe %3F symbol in the URI was considered as the argument string start.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nподдержка unix domain сокетов в модуле ngx_http_proxy_module.\n</para>\n<para lang=\"en\">\nthe unix domain sockets support in the ngx_http_proxy_module.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы ssl_engine и ssl_ciphers.<br/>\nСпасибо Сергею Скворцову за SSL-акселератор.\n</para>\n<para lang=\"en\">\nthe ssl_engine and ssl_ciphers directives.<br/>\nThanks to Sergey Skvortsov for SSL-accelerator.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.13\" date=\"2004-12-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдирективы server_names_hash и server_names_hash_threshold.\n</para>\n<para lang=\"en\">\nthe server_names_hash and server_names_hash_threshold directives.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nимена *.domain.tld в директиве server_name не работали.\n</para>\n<para lang=\"en\">\nthe *.domain.tld names in the \"server_name\" directive did not work.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпараметр лога %request_length записывал неверную длину.\n</para>\n<para lang=\"en\">\nthe %request_length log parameter logged the incorrect length.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.12\" date=\"2004-12-06\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметр лога %request_length.\n</para>\n<para lang=\"en\">\nthe %request_length log parameter.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании /dev/poll, select и poll на платформах, где возможны\nложные срабатывания указанных методов, могли быть длительные задержки\nпри обработке запроса по keep-alive соединению.\nНаблюдалось по крайней мере на Solaris с использованием /dev/poll.\n</para>\n<para lang=\"en\">\nwhen using the /dev/poll, select and poll on the platforms, where\nthese methods may do the false reports, there may be the long delay when\nthe request was passed via the keep-alive connection.\nIt may be at least on Solaris when using the /dev/poll.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива send_lowat игнорируется на Linux, так как Linux не поддерживает\nопцию SO_SNDLOWAT.\n</para>\n<para lang=\"en\">\nthe send_lowat directive is ignored on Linux because Linux does not support\nthe SO_SNDLOWAT option.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.11\" date=\"2004-12-02\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива worker_priority.\n</para>\n<para lang=\"en\">\nthe worker_priority directive.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nпод FreeBSD директивы tcp_nopush и tcp_nodelay вместе влияют на передачу\nответа.\n</para>\n<para lang=\"en\">\nboth tcp_nopush and tcp_nodelay directives affect the transferred response.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не вызывал initgroups().<br/>\nСпасибо Андрею Ситникову и Андрею Нигматулину.\n</para>\n<para lang=\"en\">\nnginx did not call initgroups().<br/>\nThanks to Andrew Sitnikov and Andrei Nigmatulin.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nngx_http_auto_index_module теперь выдаёт размер файлов в байтах.\n</para>\n<para lang=\"en\">\nnow the ngx_http_autoindex_module shows the file size in the bytes.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nngx_http_auto_index_module возвращал ошибку 500, если в каталоге есть\nбитый symlink.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module returned the 500 error if the broken symlink\nwas in a directory.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nфайлы больше 4G не передавались с использованием sendfile.\n</para>\n<para lang=\"en\">\nthe files bigger than 4G could not be transferred using sendfile.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли бэкенд резолвился в несколько адресов и при ожидании от него ответа\nпроисходила ошибка, то процесс зацикливался.\n</para>\n<para lang=\"en\">\nif the backend was resolved to several backends and there was an error while\nthe response waiting then process may got caught in an endless loop.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании метода /dev/poll рабочий процесс мог завершиться\nс сообщением \"unknown cycle\".\n</para>\n<para lang=\"en\">\nthe worker process may exit with the \"unknown cycle\" message when the /dev/poll\nmethod was used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки \"close() channel failed\".\n</para>\n<para lang=\"en\">\n\"close() channel failed\" errors.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nавтоматическое определение групп nobody и nogroup.\n</para>\n<para lang=\"en\">\nthe autodetection of the \"nobody\" and \"nogroup\" groups.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдиректива send_lowat не работала на Linux.\n</para>\n<para lang=\"en\">\nthe send_lowat directive did not work on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в конфигурации не было раздела events, то происходил segmentation fault.\n</para>\n<para lang=\"en\">\nthe segmentation fault occurred if there was no events section\nin configuration.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nnginx не собирался под OpenBSD.\n</para>\n<para lang=\"en\">\nnginx could not be built on OpenBSD.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nдвойные слэшы в \"://\" в URI превращались в \":/\".\n</para>\n<para lang=\"en\">\nthe double slashes in \"://\" in the URI were converted to \":/\".\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.10\" date=\"2004-11-26\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в запросе без аргументов есть \"//\", \"/./\", \"/../\" или \"%XX\",\nто терялся последний символ в строке запроса;\nошибка появилась в 0.1.9.\n</para>\n<para lang=\"en\">\nif the request without arguments contains \"//\", \"/./\", \"/../\" or \"%XX\"\nthen the last character in the request line was lost;\nthe bug had appeared in 0.1.9.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nисправление в версии 0.1.9 для файлов больше 2G на Linux не работало.\n</para>\n<para lang=\"en\">\nthe fix in 0.1.9 for the files bigger than 2G on Linux did not work.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.9\" date=\"2004-11-25\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nесли в запросе есть \"//\", \"/./\", \"/../\" или \"%XX\", то проксируемый\nзапрос передавался без аргументов.\n</para>\n<para lang=\"en\">\nthe proxied request was sent without arguments if the request contains\n\"//\", \"/./\", \"/../\" or \"%XX\".\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри сжатии больших ответов иногда они передавались не полностью.\n</para>\n<para lang=\"en\">\nthe large compressed responses may be transferred not completely.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nне передавались файлы больше 2G на Linux, неподдерживающем sendfile64().\n</para>\n<para lang=\"en\">\nthe files bigger than 2G was not transferred on Linux that does not support\nsendfile64().\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна Linux при конфигурации сборки нужно было обязательно использовать\nпараметр --with-poll_module;\nошибка появилась в 0.1.8.\n</para>\n<para lang=\"en\">\nwhile the build configuration on Linux the --with-poll_module parameter\nwas required;\nthe bug had appeared in 0.1.8.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.8\" date=\"2004-11-20\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка в модуле ngx_http_autoindex_module при показе длинных имён файлов.\n</para>\n<para lang=\"en\">\nin the ngx_http_autoindex_module if the long file names were in the listing.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодификатор \"^~\" в директиве location.\n</para>\n<para lang=\"en\">\nthe \"^~\" modifier in the location directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_max_temp_file_size.\n</para>\n<para lang=\"en\">\nthe proxy_max_temp_file_size directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.7\" date=\"2004-11-12\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании sendfile, если передаваемый файл менялся, то мог\nпроизойти segmentation fault на FreeBSD;\nошибка появилась в 0.1.5.\n</para>\n<para lang=\"en\">\non FreeBSD the segmentation fault may occur if the size of the transferred\nfile was changed;\nthe bug had appeared in 0.1.5.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.6\" date=\"2004-11-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри некоторых комбинациях директив location c регулярными выражениями\nиспользовалась конфигурация не из того location.\n</para>\n<para lang=\"en\">\nsome location directive combinations with the regular expressions caused\nthe wrong configuration choose.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.5\" date=\"2004-11-11\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна Solaris и Linux могло быть очень много сообщений \"recvmsg() returned\nnot enough data\".\n</para>\n<para lang=\"en\">\non Solaris and Linux there may be too many \"recvmsg() returned not enough data\"\nalerts.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв режиме прокси без использования sendfile на Solaris возникала\nошибка \"writev() failed <nobr>(22: Invalid argument)\".</nobr>\nНа других платформах, не поддерживающих sendfile, процесс зацикливался.\n</para>\n<para lang=\"en\">\nthere were the \"writev() failed <nobr>(22: Invalid argument)\"</nobr> errors on\nSolaris in proxy mode without sendfile. On other platforms that do not\nsupport sendfile at all the process got caught in an endless loop.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании sendfile в режиме прокси на Solaris возникал\nsegmentation fault.\n</para>\n<para lang=\"en\">\nsegmentation fault on Solaris in proxy mode and using sendfile.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nsegmentation fault на Solaris.\n</para>\n<para lang=\"en\">\nsegmentation fault on Solaris.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nобновление исполняемого файла на лету не работало на Linux.\n</para>\n<para lang=\"en\">\non-line upgrade did not work on Linux.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nв списке файлов, выдаваемом модулем ngx_http_autoindex_module,\nне перекодировались пробелы, кавычки и знаки процента.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module module did not escape the spaces,\nthe quotes, and the percent signs in the directory listing.\n</para>\n</change>\n\n<change type=\"change\">\n<para lang=\"ru\">\nуменьшение операций копирования.\n</para>\n<para lang=\"en\">\nthe decrease of the copy operations.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива userid_p3p.\n</para>\n<para lang=\"en\">\nthe userid_p3p directive.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.4\" date=\"2004-10-26\">\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка в модуле ngx_http_autoindex_module.\n</para>\n<para lang=\"en\">\nin the ngx_http_autoindex_module.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.3\" date=\"2004-10-25\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nмодуль ngx_http_autoindex_module и директива autoindex.\n</para>\n<para lang=\"en\">\nthe ngx_http_autoindex_module and the autoindex directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива proxy_set_x_url.\n</para>\n<para lang=\"en\">\nthe proxy_set_x_url directive.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль проксировании мог привести к зацикливанию, если не использовался\nsendfile.\n</para>\n<para lang=\"en\">\nproxy module may get caught in an endless loop when sendfile is not used.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.2\" date=\"2004-10-21\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nпараметры --user=USER, --group=GROUP и --with-ld-opt=OPTIONS в configure.\n</para>\n<para lang=\"en\">\nthe --user=USER, --group=GROUP, and --with-ld-opt=OPTIONS options in configure.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива server_name поддерживает *.domain.tld.\n</para>\n<para lang=\"en\">\nthe server_name directive supports *.domain.tld.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nулучшена переносимость на неизвестные платформы.\n</para>\n<para lang=\"en\">\nthe portability improvements.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nнельзя переконфигурировать nginx, если конфигурационный файл указан\nв командной строке;\nошибка появилась в 0.1.1.\n</para>\n<para lang=\"en\">\nif configuration file was set in command line, the reconfiguration\nwas impossible;\nthe bug had appeared in 0.1.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль проксировании мог привести к зацикливанию, если не использовался\nsendfile.\n</para>\n<para lang=\"en\">\nproxy module may get caught in an endless loop when sendfile is not used.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри использовании sendfile текст ответа не перекодировался\nсогласно директивам модуля charset;\nошибка появилась в 0.1.1.\n</para>\n<para lang=\"en\">\nwith sendfile the response was not recoded according to the charset\nmodule directives;\nthe bug had appeared in 0.1.1.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nочень редкая ошибка при обработке kqueue.\n</para>\n<para lang=\"en\">\nvery seldom bug in the kqueue processing.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nмодуль сжатия сжимал уже сжатые ответы, полученные при проксировании.\n</para>\n<para lang=\"en\">\nthe gzip module compressed the proxied responses that was already compressed.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.1\" date=\"2004-10-11\">\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива gzip_types.\n</para>\n<para lang=\"en\">\nthe gzip_types directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива tcp_nodelay.\n</para>\n<para lang=\"en\">\nthe tcp_nodelay directive.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nдиректива send_lowat работает не только на платформах, поддерживающих\nkqueue NOTE_LOWAT, но и на всех, поддерживающих SO_SNDLOWAT.\n</para>\n<para lang=\"en\">\nthe send_lowat directive is working not only on OSes that support\nkqueue NOTE_LOWAT, but also on OSes that support SO_SNDLOWAT.\n</para>\n</change>\n\n<change type=\"feature\">\n<para lang=\"ru\">\nэмуляция setproctitle() для Linux и Solaris.\n</para>\n<para lang=\"en\">\nthe setproctitle() emulation for Linux and Solaris.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка при переписывании заголовка \"Location\" при проксировании.\n</para>\n<para lang=\"en\">\nthe \"Location\" header rewrite bug fixed while the proxying.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибка в модуле ngx_http_chunked_module, приводившая к зацикливанию.\n</para>\n<para lang=\"en\">\nthe ngx_http_chunked_module module may get caught in an endless loop.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nошибки в модуле /dev/poll.\n</para>\n<para lang=\"en\">\nthe /dev/poll module bugs fixed.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nпри проксировании и использовании временных файлов ответы портились.\n</para>\n<para lang=\"en\">\nthe responses were corrupted when the temporary files were used\nwhile the proxying.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nбэкенду передавались запросы с неперекодированными символами.\n</para>\n<para lang=\"en\">\nthe unescaped requests were passed to the backend.\n</para>\n</change>\n\n<change type=\"bugfix\">\n<para lang=\"ru\">\nна Linux 2.4 при конфигурации сборки нужно было обязательно использовать\nпараметр --with-poll_module.\n</para>\n<para lang=\"en\">\nwhile the build configuration on Linux 2.4 the --with-poll_module parameter\nwas required.\n</para>\n</change>\n\n</changes>\n\n\n<changes ver=\"0.1.0\" date=\"2004-10-04\">\n\n<change>\n<para lang=\"ru\">\nПервая публично доступная версия.\n</para>\n<para lang=\"en\">\nThe first public version.\n</para>\n</change>\n\n</changes>\n\n\n</change_log>\n"
  },
  {
    "path": "docs/xsls/changes.xsls",
    "content": "X:stylesheet {\n\nX:output method=\"text\";\n\nX:param lang=\"'en'\";\nX:param configuration=\"'../xml/change_log_conf.xml'\";\n\nX:var conf = \"document($configuration)/configuration\";\nX:var start = \"$conf/start\";\nX:var indent = \"$conf/indent\";\nX:var max = \"$conf/length\";\nX:var br = {&lt;br&gt;}\n\n\nX:template = \"/\" { !! \"change_log\"; }\nX:template = \"change_log\" { !! \"changes\"; }\n\n\nX:template = \"changes\" {\n    X:text {&#10;}\n\n    !{substring(concat($conf/changes[@lang=$lang]/title,\n                       //change_log/@title,\n                       ' ', @ver,\n                       '                                                    '),\n                1, $conf/changes[@lang=$lang]/length)}\n\n    X:if \"$lang='ru'\" {\n        !{substring(@date, 9, 2)}\n        X:text {.}\n        !{substring(@date, 6, 2)}\n        X:text {.}\n        !{substring(@date, 1, 4)}\n    }\n\n    X:if \"$lang='en'\" {\n        !{substring(@date, 9, 2)}\n        !{$conf/changes[@lang=$lang]/month[number(substring(current()/@date,\n                                                            6, 2))]}\n        !{substring(@date, 1, 4)}\n    }\n\n    X:text {&#10;}\n\n    !! \"change\";\n\n    X:text {&#10;}\n}\n\n\nX:template = \"change\" {\n    X:var prefix = \"$conf/changes[@lang=$lang]/*[local-name(.)=current()/@type]\"\n\n    X:var postfix = { X:if \"$prefix\" { X:text {: } } }\n\n    !! \"para[@lang=$lang]\" (prefix = \"concat($start, $prefix, $postfix)\");\n}\n\n\nX:template para(prefix) = \"para\" {\n    X:var text = { !!; }\n\n    X:text {&#10;}\n\n    !wrap(text = \"normalize-space($text)\",\n          prefix = { X:if \"position() = 1\" { !{$prefix} } else { !{$indent} } })\n}\n\n\nX:template wrap(text, prefix) {\n    X:if \"$text\" {\n        X:var offset = {\n            X:choose {\n                X:when \"starts-with($text, concat($br, ' '))\" {\n                    !{string-length($br) + 2}\n                }\n                X:when \"starts-with($text, $br)\" {\n                    !{string-length($br) + 1}\n                }\n                X:otherwise {\n                    1\n                }\n            }\n        }\n\n        X:var length = {\n            !length(text = \"substring($text, $offset)\",\n                    prefix = \"string-length($prefix)\",\n                    length = \"$max\")\n        }\n\n        !{$prefix}\n\n        !{normalize-space(translate(substring($text, $offset, $length),\n                                    '&#xA0;', ' '))}\n\n        X:text {&#10;}\n\n        !wrap(text = \"substring($text, $length + $offset)\", prefix = \"$indent\")\n    }\n}\n\n\nX:template length(text, prefix, length) {\n    X:var break = \"substring-before(substring($text, 1,\n                                    $length - $prefix + string-length($br)),\n                                    $br)\"\n\n    X:choose {\n        X:when \"$break\" { !{string-length($break)} }\n\n        X:when \"$length = 0\" { !{$max - $prefix} }\n\n        X:when \"string-length($text) + $prefix &lt;= $length\" {\n            !{$length - $prefix}\n        }\n\n        X:when \"substring($text, $length - $prefix + 1, 1) = ' '\" {\n            !{$length - $prefix + 1}\n        }\n\n        X:otherwise {\n            !length(text = \"$text\", prefix = \"$prefix\", length = \"$length - 1\")\n        }\n    }\n}\n\n\nX:template = \"at\" {@}\nX:template = \"br\" { !{$br} }\nX:template = \"nobr\" { !{translate(., ' ', '&#xA0;')} }\n\n\n}\n"
  },
  {
    "path": "docs/xslt/changes.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">\n\n<xsl:output method=\"text\"/>\n\n<xsl:param select=\"'en'\" name=\"lang\"/>\n<xsl:param select=\"'../xml/change_log_conf.xml'\" name=\"configuration\"/>\n\n<xsl:variable select=\"document($configuration)/configuration\" name=\"conf\"/>\n<xsl:variable select=\"$conf/start\" name=\"start\"/>\n<xsl:variable select=\"$conf/indent\" name=\"indent\"/>\n<xsl:variable select=\"$conf/length\" name=\"max\"/>\n<xsl:variable name=\"br\">&lt;br&gt;</xsl:variable>\n\n\n<xsl:template match=\"/\"> <xsl:apply-templates select=\"change_log\"/> </xsl:template>\n<xsl:template match=\"change_log\"> <xsl:apply-templates select=\"changes\"/> </xsl:template>\n\n\n<xsl:template match=\"changes\">\n    <xsl:text>&#10;</xsl:text>\n\n    <xsl:value-of select=\"substring(concat($conf/changes[@lang=$lang]/title,\n                       //change_log/@title,\n                       ' ', @ver,\n                       '                                                    '),\n                1, $conf/changes[@lang=$lang]/length)\"/>\n\n    <xsl:if test=\"$lang='ru'\">\n        <xsl:value-of select=\"substring(@date, 9, 2)\"/>\n        <xsl:text>.</xsl:text>\n        <xsl:value-of select=\"substring(@date, 6, 2)\"/>\n        <xsl:text>.</xsl:text>\n        <xsl:value-of select=\"substring(@date, 1, 4)\"/>\n    </xsl:if>\n\n    <xsl:if test=\"$lang='en'\">\n        <xsl:value-of select=\"substring(@date, 9, 2)\"/>\n        <xsl:value-of select=\"$conf/changes[@lang=$lang]/month[number(substring(current()/@date,\n                                                            6, 2))]\"/>\n        <xsl:value-of select=\"substring(@date, 1, 4)\"/>\n    </xsl:if>\n\n    <xsl:text>&#10;</xsl:text>\n\n    <xsl:apply-templates select=\"change\"/>\n\n    <xsl:text>&#10;</xsl:text>\n</xsl:template>\n\n\n<xsl:template match=\"change\">\n    <xsl:variable select=\"$conf/changes[@lang=$lang]/*[local-name(.)=current()/@type]\" name=\"prefix\"/>\n\n    <xsl:variable name=\"postfix\"> <xsl:if test=\"$prefix\"> <xsl:text>: </xsl:text> </xsl:if> </xsl:variable>\n\n    <xsl:apply-templates select=\"para[@lang=$lang]\"><xsl:with-param select=\"concat($start, $prefix, $postfix)\" name=\"prefix\"/></xsl:apply-templates>\n</xsl:template>\n\n\n<xsl:template name=\"para\" match=\"para\"><xsl:param name=\"prefix\"/>\n    <xsl:variable name=\"text\"> <xsl:apply-templates/> </xsl:variable>\n\n    <xsl:text>&#10;</xsl:text>\n\n    <xsl:call-template name=\"wrap\"><xsl:with-param select=\"normalize-space($text)\" name=\"text\"/><xsl:with-param name=\"prefix\"> <xsl:choose><xsl:when test=\"position() = 1\"> <xsl:value-of select=\"$prefix\"/> </xsl:when><xsl:otherwise> <xsl:value-of select=\"$indent\"/> </xsl:otherwise></xsl:choose> </xsl:with-param></xsl:call-template></xsl:template>\n\n\n<xsl:template name=\"wrap\"><xsl:param name=\"text\"/><xsl:param name=\"prefix\"/>\n    <xsl:if test=\"$text\">\n        <xsl:variable name=\"offset\">\n            <xsl:choose>\n                <xsl:when test=\"starts-with($text, concat($br, ' '))\">\n                    <xsl:value-of select=\"string-length($br) + 2\"/>\n                </xsl:when>\n                <xsl:when test=\"starts-with($text, $br)\">\n                    <xsl:value-of select=\"string-length($br) + 1\"/>\n                </xsl:when>\n                <xsl:otherwise>\n                    1\n                </xsl:otherwise>\n            </xsl:choose>\n        </xsl:variable>\n\n        <xsl:variable name=\"length\">\n            <xsl:call-template name=\"length\"><xsl:with-param select=\"substring($text, $offset)\" name=\"text\"/><xsl:with-param select=\"string-length($prefix)\" name=\"prefix\"/><xsl:with-param select=\"$max\" name=\"length\"/></xsl:call-template></xsl:variable>\n\n        <xsl:value-of select=\"$prefix\"/>\n\n        <xsl:value-of select=\"normalize-space(translate(substring($text, $offset, $length),\n                                    '&#xA0;', ' '))\"/>\n\n        <xsl:text>&#10;</xsl:text>\n\n        <xsl:call-template name=\"wrap\"><xsl:with-param select=\"substring($text, $length + $offset)\" name=\"text\"/><xsl:with-param select=\"$indent\" name=\"prefix\"/></xsl:call-template></xsl:if>\n</xsl:template>\n\n\n<xsl:template name=\"length\"><xsl:param name=\"text\"/><xsl:param name=\"prefix\"/><xsl:param name=\"length\"/>\n    <xsl:variable select=\"substring-before(substring($text, 1,\n                                    $length - $prefix + string-length($br)),\n                                    $br)\" name=\"break\"/>\n\n    <xsl:choose>\n        <xsl:when test=\"$break\"> <xsl:value-of select=\"string-length($break)\"/> </xsl:when>\n\n        <xsl:when test=\"$length = 0\"> <xsl:value-of select=\"$max - $prefix\"/> </xsl:when>\n\n        <xsl:when test=\"string-length($text) + $prefix &lt;= $length\">\n            <xsl:value-of select=\"$length - $prefix\"/>\n        </xsl:when>\n\n        <xsl:when test=\"substring($text, $length - $prefix + 1, 1) = ' '\">\n            <xsl:value-of select=\"$length - $prefix + 1\"/>\n        </xsl:when>\n\n        <xsl:otherwise>\n            <xsl:call-template name=\"length\"><xsl:with-param select=\"$text\" name=\"text\"/><xsl:with-param select=\"$prefix\" name=\"prefix\"/><xsl:with-param select=\"$length - 1\" name=\"length\"/></xsl:call-template></xsl:otherwise>\n    </xsl:choose>\n</xsl:template>\n\n\n<xsl:template match=\"at\">@</xsl:template>\n<xsl:template match=\"br\"> <xsl:value-of select=\"$br\"/> </xsl:template>\n<xsl:template match=\"nobr\"> <xsl:value-of select=\"translate(., ' ', '&#xA0;')\"/> </xsl:template>\n\n\n</xsl:stylesheet>\n"
  },
  {
    "path": "misc/GNUmakefile",
    "content": "\nVER =\t\t$(shell grep 'define NGINX_VERSION' src/core/nginx.h\t\\\n\t\t\t| sed -e 's/^.*\"\\(.*\\)\".*/\\1/')\nNGINX =\t\tnginx-$(VER)\nTEMP =\t\ttmp\n\nCC =\t\tcl\nOBJS =\t\tobjs.msvc8\nOPENSSL =\topenssl-1.1.1l\nZLIB =\t\tzlib-1.2.11\nPCRE =\t\tpcre-8.44\n\n\nrelease: export\n\n\tmv $(TEMP)/$(NGINX)/auto/configure $(TEMP)/$(NGINX)\n\n\t# delete incomplete sources\n\trm $(TEMP)/$(NGINX)/src/event/ngx_event_acceptex.c\n\trm $(TEMP)/$(NGINX)/src/event/ngx_event_connectex.c\n\trm $(TEMP)/$(NGINX)/src/event/modules/ngx_iocp_module.*\n\trm -r $(TEMP)/$(NGINX)/src/os/win32\n\n\tmv $(TEMP)/$(NGINX)/docs/text/LICENSE $(TEMP)/$(NGINX)\n\tmv $(TEMP)/$(NGINX)/docs/text/README $(TEMP)/$(NGINX)\n\tmv $(TEMP)/$(NGINX)/docs/html $(TEMP)/$(NGINX)\n\tmv $(TEMP)/$(NGINX)/docs/man $(TEMP)/$(NGINX)\n\n\t$(MAKE) -f docs/GNUmakefile changes\n\n\trm -r $(TEMP)/$(NGINX)/docs\n\trm -r $(TEMP)/$(NGINX)/misc\n\n\ttar -c -z -f $(NGINX).tar.gz --directory $(TEMP) $(NGINX)\n\n\nexport:\n\trm -rf $(TEMP)\n\thg archive -X '.hg*' $(TEMP)/$(NGINX)\n\n\nRELEASE:\n\thg ci -m nginx-$(VER)-RELEASE\n\thg tag -m \"release-$(VER) tag\" release-$(VER)\n\n\t$(MAKE) -f misc/GNUmakefile release\n\n\nwin32:\n\t./auto/configure\t\t\t\t\t\t\\\n\t\t--with-cc=$(CC)\t\t\t\t\t\t\\\n\t\t--builddir=$(OBJS)\t\t\t\t\t\\\n\t\t--with-debug\t\t\t\t\t\t\\\n\t\t--prefix= \t\t\t\t\t\t\\\n\t\t--conf-path=conf/nginx.conf\t\t\t\t\\\n\t\t--pid-path=logs/nginx.pid\t\t\t\t\\\n\t\t--http-log-path=logs/access.log\t\t\t\t\\\n\t\t--error-log-path=logs/error.log\t\t\t\t\\\n\t\t--sbin-path=nginx.exe\t\t\t\t\t\\\n\t\t--http-client-body-temp-path=temp/client_body_temp\t\\\n\t\t--http-proxy-temp-path=temp/proxy_temp\t\t\t\\\n\t\t--http-fastcgi-temp-path=temp/fastcgi_temp\t\t\\\n\t\t--http-scgi-temp-path=temp/scgi_temp\t\t\t\\\n\t\t--http-uwsgi-temp-path=temp/uwsgi_temp\t\t\t\\\n\t\t--with-cc-opt=-DFD_SETSIZE=1024\t\t\t\t\\\n\t\t--with-pcre=$(OBJS)/lib/$(PCRE)\t\t\t\t\\\n\t\t--with-zlib=$(OBJS)/lib/$(ZLIB)\t\t\t\t\\\n\t\t--with-http_v2_module\t\t\t\t\t\\\n\t\t--with-http_realip_module\t\t\t\t\\\n\t\t--with-http_addition_module\t\t\t\t\\\n\t\t--with-http_sub_module\t\t\t\t\t\\\n\t\t--with-http_dav_module\t\t\t\t\t\\\n\t\t--with-http_stub_status_module\t\t\t\t\\\n\t\t--with-http_flv_module\t\t\t\t\t\\\n\t\t--with-http_mp4_module\t\t\t\t\t\\\n\t\t--with-http_gunzip_module\t\t\t\t\\\n\t\t--with-http_gzip_static_module\t\t\t\t\\\n\t\t--with-http_auth_request_module\t\t\t\t\\\n\t\t--with-http_random_index_module\t\t\t\t\\\n\t\t--with-http_secure_link_module\t\t\t\t\\\n\t\t--with-http_slice_module\t\t\t\t\\\n\t\t--with-mail\t\t\t\t\t\t\\\n\t\t--with-stream\t\t\t\t\t\t\\\n\t\t--with-openssl=$(OBJS)/lib/$(OPENSSL)\t\t\t\\\n\t\t--with-openssl-opt=\"no-asm no-tests -D_WIN32_WINNT=0x0501\" \\\n\t\t--with-http_ssl_module\t\t\t\t\t\\\n\t\t--with-mail_ssl_module\t\t\t\t\t\\\n\t\t--with-stream_ssl_module\n\n\nzip: export\n\trm -f $(NGINX).zip\n\n\tmkdir -p $(TEMP)/$(NGINX)/docs.new\n\tmkdir -p $(TEMP)/$(NGINX)/logs\n\tmkdir -p $(TEMP)/$(NGINX)/temp\n\n\tsed -i '' -e \"s/$$/`printf '\\r'`/\" $(TEMP)/$(NGINX)/conf/*\n\n\tmv $(TEMP)/$(NGINX)/docs/text/LICENSE $(TEMP)/$(NGINX)/docs.new\n\tmv $(TEMP)/$(NGINX)/docs/text/README $(TEMP)/$(NGINX)/docs.new\n\tmv $(TEMP)/$(NGINX)/docs/html $(TEMP)/$(NGINX)\n\n\trm -r $(TEMP)/$(NGINX)/docs\n\tmv $(TEMP)/$(NGINX)/docs.new $(TEMP)/$(NGINX)/docs\n\n\tcp -p $(OBJS)/nginx.exe $(TEMP)/$(NGINX)\n\n\t$(MAKE) -f docs/GNUmakefile changes\n\tmv $(TEMP)/$(NGINX)/CHANGES* $(TEMP)/$(NGINX)/docs/\n\n\tcp -p $(OBJS)/lib/$(OPENSSL)/LICENSE\t\t\t\t\\\n\t\t$(TEMP)/$(NGINX)/docs/OpenSSL.LICENSE\n\n\tcp -p $(OBJS)/lib/$(PCRE)/LICENCE\t\t\t\t\\\n\t\t$(TEMP)/$(NGINX)/docs/PCRE.LICENCE\n\n\tsed -ne '/^ (C) 1995-20/,/^  jloup@gzip\\.org/p'\t\t\t\\\n\t\t$(OBJS)/lib/$(ZLIB)/README\t\t\t\t\\\n\t\t> $(TEMP)/$(NGINX)/docs/zlib.LICENSE\n\n\ttouch -r $(OBJS)/lib/$(ZLIB)/README\t\t\t\t\\\n\t\t$(TEMP)/$(NGINX)/docs/zlib.LICENSE\n\n\trm -r $(TEMP)/$(NGINX)/auto\n\trm -r $(TEMP)/$(NGINX)/misc\n\trm -r $(TEMP)/$(NGINX)/src\n\n\tcd $(TEMP) && zip -r ../$(NGINX).zip $(NGINX)\n\n\nicons:\tsrc/os/win32/nginx.ico\n\n# 48x48, 32x32 and 16x16 icons\n\nsrc/os/win32/nginx.ico:\tsrc/os/win32/nginx_icon48.xpm\t\t\t\\\n\t\t\tsrc/os/win32/nginx_icon32.xpm\t\t\t\\\n\t\t\tsrc/os/win32/nginx_icon16.xpm\n\n\ttest -d $(TEMP) || mkdir $(TEMP)\n\n\txpmtoppm --alphaout=$(TEMP)/nginx48.pbm\t\t\t\t\\\n\t\tsrc/os/win32/nginx_icon48.xpm > $(TEMP)/nginx48.ppm\n\n\txpmtoppm --alphaout=$(TEMP)/nginx32.pbm\t\t\t\t\\\n\t\tsrc/os/win32/nginx_icon32.xpm > $(TEMP)/nginx32.ppm\n\n\txpmtoppm --alphaout=$(TEMP)/nginx16.pbm\t\t\t\t\\\n\t\tsrc/os/win32/nginx_icon16.xpm > $(TEMP)/nginx16.ppm\n\n\tppmtowinicon -output src/os/win32/nginx.ico -andpgms\t\t\\\n\t\t$(TEMP)/nginx48.ppm $(TEMP)/nginx48.pbm\t\t\t\\\n\t\t$(TEMP)/nginx32.ppm $(TEMP)/nginx32.pbm\t\t\t\\\n\t\t$(TEMP)/nginx16.ppm $(TEMP)/nginx16.pbm\n"
  },
  {
    "path": "misc/README",
    "content": "\nmake -f misc/GNUmakefile release\n\nthe required tools:\n*) xsltproc to build CHANGES,\n*) xslscript.pl ( http://hg.nginx.org/xslscript ) to build XSLTs\n   from XSLScript sources.\n\n\nmake -f misc/GNUmakefile icons\n\nthe required tool:\n*) netpbm to create Win32 icons from xpm sources.\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\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      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;\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    ngx_ssl_init(log);\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    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 (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    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        );\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        if (ls[i].ignore || ls[i].quic) {\n            continue;\n        }\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                break;\n\n            case 'T':\n                ngx_test_config = 1;\n                ngx_dump_config = 1;\n                break;\n\n            case 'q':\n                ngx_quiet_mode = 1;\n                break;\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 (*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                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    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, 1);\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    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"
  },
  {
    "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      1021004\n#define NGINX_VERSION      \"1.21.4\"\n#define NGINX_VER          \"nginx/\" NGINX_VERSION\n\n#ifdef NGX_BUILD\n#define NGINX_VER_BUILD    NGINX_VER \" (\" NGX_BUILD \")\"\n#else\n#define NGINX_VER_BUILD    NGINX_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_bpf.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#define NGX_BPF_LOGBUF_SIZE  (16 * 1024)\n\n\nstatic ngx_inline int\nngx_bpf(enum bpf_cmd cmd, union bpf_attr *attr, unsigned int size)\n{\n    return syscall(__NR_bpf, cmd, attr, size);\n}\n\n\nvoid\nngx_bpf_program_link(ngx_bpf_program_t *program, const char *symbol, int fd)\n{\n    ngx_uint_t        i;\n    ngx_bpf_reloc_t  *rl;\n\n    rl = program->relocs;\n\n    for (i = 0; i < program->nrelocs; i++) {\n        if (ngx_strcmp(rl[i].name, symbol) == 0) {\n            program->ins[rl[i].offset].src_reg = 1;\n            program->ins[rl[i].offset].imm = fd;\n        }\n    }\n}\n\n\nint\nngx_bpf_load_program(ngx_log_t *log, ngx_bpf_program_t *program)\n{\n    int             fd;\n    union bpf_attr  attr;\n#if (NGX_DEBUG)\n    char            buf[NGX_BPF_LOGBUF_SIZE];\n#endif\n\n    ngx_memzero(&attr, sizeof(union bpf_attr));\n\n    attr.license = (uintptr_t) program->license;\n    attr.prog_type = program->type;\n    attr.insns = (uintptr_t) program->ins;\n    attr.insn_cnt = program->nins;\n\n#if (NGX_DEBUG)\n    /* for verifier errors */\n    attr.log_buf = (uintptr_t) buf;\n    attr.log_size = NGX_BPF_LOGBUF_SIZE;\n    attr.log_level = 1;\n#endif\n\n    fd = ngx_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));\n    if (fd < 0) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"failed to load BPF program\");\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,\n                       \"bpf verifier: %s\", buf);\n\n        return -1;\n    }\n\n    return fd;\n}\n\n\nint\nngx_bpf_map_create(ngx_log_t *log, enum bpf_map_type type, int key_size,\n    int value_size, int max_entries, uint32_t map_flags)\n{\n    int             fd;\n    union bpf_attr  attr;\n\n    ngx_memzero(&attr, sizeof(union bpf_attr));\n\n    attr.map_type = type;\n    attr.key_size = key_size;\n    attr.value_size = value_size;\n    attr.max_entries = max_entries;\n    attr.map_flags = map_flags;\n\n    fd = ngx_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));\n    if (fd < 0) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"failed to create BPF map\");\n        return NGX_ERROR;\n    }\n\n    return fd;\n}\n\n\nint\nngx_bpf_map_update(int fd, const void *key, const void *value, uint64_t flags)\n{\n    union bpf_attr attr;\n\n    ngx_memzero(&attr, sizeof(union bpf_attr));\n\n    attr.map_fd = fd;\n    attr.key = (uintptr_t) key;\n    attr.value = (uintptr_t) value;\n    attr.flags = flags;\n\n    return ngx_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));\n}\n\n\nint\nngx_bpf_map_delete(int fd, const void *key)\n{\n    union bpf_attr attr;\n\n    ngx_memzero(&attr, sizeof(union bpf_attr));\n\n    attr.map_fd = fd;\n    attr.key = (uintptr_t) key;\n\n    return ngx_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));\n}\n\n\nint\nngx_bpf_map_lookup(int fd, const void *key, void *value)\n{\n    union bpf_attr attr;\n\n    ngx_memzero(&attr, sizeof(union bpf_attr));\n\n    attr.map_fd = fd;\n    attr.key = (uintptr_t) key;\n    attr.value = (uintptr_t) value;\n\n    return ngx_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));\n}\n"
  },
  {
    "path": "src/core/ngx_bpf.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_BPF_H_INCLUDED_\n#define _NGX_BPF_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#include <linux/bpf.h>\n\n\ntypedef struct {\n    char                *name;\n    int                  offset;\n} ngx_bpf_reloc_t;\n\ntypedef struct {\n    char                *license;\n    enum bpf_prog_type   type;\n    struct bpf_insn     *ins;\n    size_t               nins;\n    ngx_bpf_reloc_t     *relocs;\n    size_t               nrelocs;\n} ngx_bpf_program_t;\n\n\nvoid ngx_bpf_program_link(ngx_bpf_program_t *program, const char *symbol,\n    int fd);\nint ngx_bpf_load_program(ngx_log_t *log, ngx_bpf_program_t *program);\n\nint ngx_bpf_map_create(ngx_log_t *log, enum bpf_map_type type, int key_size,\n    int value_size, int max_entries, uint32_t map_flags);\nint ngx_bpf_map_update(int fd, const void *key, const void *value,\n    uint64_t flags);\nint ngx_bpf_map_delete(int fd, const void *key);\nint ngx_bpf_map_lookup(int fd, const void *key, void *value);\n\n#endif /* _NGX_BPF_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#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)\n    ssize_t                    (*aio_preload)(ngx_buf_t *file);\n#endif\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};\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#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                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].quic) {\n                if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                    \"fcntl(FD_CLOEXEC) \\\"%s\\\" failed\", &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                    if (!ngx_test_config) {\n                        failed = 1;\n                    }\n                }\n            }\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://bugzilla.openvz.org/show_bug.cgi?id=2470\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 (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 (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#if (NGX_QUIC)\n        if (ls[i].quic) {\n            continue;\n        }\n#endif\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\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    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\n    ngx_memzero(c, sizeof(ngx_connection_t));\n\n    c->read = rev;\n    c->write = wev;\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\n    rev->instance = !instance;\n    wev->instance = !instance;\n\n    rev->index = NGX_INVALID_INDEX;\n    wev->index = NGX_INVALID_INDEX;\n\n    rev->data = c;\n    wev->data = c;\n\n    wev->write = 1;\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 (!c->shared) {\n        if (ngx_del_conn) {\n            ngx_del_conn(c, NGX_CLOSE_EVENT);\n\n        } else {\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    c->read->closed = 1;\n    c->write->closed = 1;\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\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\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    /* 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    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\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    unsigned            quic:1;\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    ngx_event_t        *read;\n    ngx_event_t        *write;\n\n    ngx_socket_t        fd;\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\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_QUIC || NGX_COMPAT)\n    ngx_quic_stream_t     *quic;\n#endif\n\n#if (NGX_SSL || NGX_COMPAT)\n    ngx_ssl_connection_t  *ssl;\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\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\n#if (NGX_HAVE_AIO_SENDFILE || 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\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_quic_stream_s     ngx_quic_stream_t;\ntypedef struct ngx_ssl_connection_s  ngx_ssl_connection_t;\ntypedef struct ngx_udp_connection_s  ngx_udp_connection_t;\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\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_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_radix_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#if (NGX_QUIC)\n#include <ngx_event_quic.h>\n#endif\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 (NGX_HAVE_BPF)\n#include <ngx_bpf.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\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\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\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\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\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 (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    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\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    /* 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\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\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};\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} 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\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 {\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;\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\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 (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\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        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    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\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        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    } 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\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"
  },
  {
    "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\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\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      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\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    } 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"
  },
  {
    "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/*********************************/\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/*********************************/\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);\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);\n\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;\nstatic ngx_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_AIO_SENDFILE || 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);\n#if (NGX_HAVE_AIO_SENDFILE)\nstatic ngx_int_t ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx,\n    ngx_file_t *file);\n#endif\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    if (buf->in_file && buf->file->directio) {\n        return 0;\n    }\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 (!sendfile) {\n\n        if (!ngx_buf_in_memory(buf)) {\n            return 0;\n        }\n\n        buf->in_file = 0;\n    }\n\n#if (NGX_HAVE_AIO_SENDFILE)\n    if (ctx->aio_preload && buf->in_file) {\n        (void) ngx_output_chain_aio_setup(ctx, buf->file);\n    }\n#endif\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\n#if (NGX_HAVE_AIO_SENDFILE)\n\nstatic ngx_int_t\nngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)\n{\n    ngx_event_aio_t  *aio;\n\n    if (file->aio == NULL && ngx_file_aio_init(file, ctx->pool) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    aio = file->aio;\n\n    aio->data = ctx->filter_ctx;\n    aio->preload_handler = ctx->aio_preload;\n\n    return NGX_OK;\n}\n\n#endif\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\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\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    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)  ((p)[0] << 8 | (p)[1])\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\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);\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        && 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    ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                  \"broken header: \\\"%*s\\\"\", (size_t) (last - 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_MAX_HEADER) {\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        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        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        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        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        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"PROXY protocol v2 %z bytes of tlv ignored\", end - buf);\n    }\n\n    c->proxy_protocol = pp;\n\n    return end;\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_MAX_HEADER  107\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};\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);\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_regex_conf_t;\n\n\nstatic void * ngx_libc_cdecl ngx_regex_malloc(size_t size);\nstatic void ngx_libc_cdecl ngx_regex_free(void *p);\n#if (NGX_HAVE_PCRE_JIT)\nstatic void ngx_pcre_free_studies(void *data);\n#endif\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_pcre_pool;\nstatic ngx_list_t  *ngx_pcre_studies;\n\n\nvoid\nngx_regex_init(void)\n{\n    pcre_malloc = ngx_regex_malloc;\n    pcre_free = ngx_regex_free;\n}\n\n\nstatic ngx_inline void\nngx_regex_malloc_init(ngx_pool_t *pool)\n{\n    ngx_pcre_pool = pool;\n}\n\n\nstatic ngx_inline void\nngx_regex_malloc_done(void)\n{\n    ngx_pcre_pool = NULL;\n}\n\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_regex_elt_t  *elt;\n\n    ngx_regex_malloc_init(rc->pool);\n\n    re = pcre_compile((const char *) rc->pattern.data, (int) rc->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_pcre_studies != NULL) {\n        elt = ngx_list_push(ngx_pcre_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\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\nstatic void * ngx_libc_cdecl\nngx_regex_malloc(size_t size)\n{\n    ngx_pool_t      *pool;\n    pool = ngx_pcre_pool;\n\n    if (pool) {\n        return ngx_palloc(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\n#if (NGX_HAVE_PCRE_JIT)\n\nstatic void\nngx_pcre_free_studies(void *data)\n{\n    ngx_list_t *studies = data;\n\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_regex_elt_t  *elts;\n\n    part = &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 (elts[i].regex->extra != NULL) {\n            pcre_free_study(elts[i].regex->extra);\n        }\n    }\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_regex_module_init(ngx_cycle_t *cycle)\n{\n    int               opt;\n    const char       *errstr;\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_regex_elt_t  *elts;\n\n    opt = 0;\n\n#if (NGX_HAVE_PCRE_JIT)\n    {\n    ngx_regex_conf_t    *rcf;\n    ngx_pool_cleanup_t  *cln;\n\n    rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);\n\n    if (rcf->pcre_jit) {\n        opt = PCRE_STUDY_JIT_COMPILE;\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.\n         */\n\n        cln = ngx_pool_cleanup_add(cycle->pool, 0);\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        cln->handler = ngx_pcre_free_studies;\n        cln->data = ngx_pcre_studies;\n    }\n    }\n#endif\n\n    ngx_regex_malloc_init(cycle->pool);\n\n    part = &ngx_pcre_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        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    }\n\n    ngx_regex_malloc_done();\n\n    ngx_pcre_studies = NULL;\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\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    ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t));\n    if (ngx_pcre_studies == NULL) {\n        return NULL;\n    }\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_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#include <pcre.h>\n\n\n#define NGX_REGEX_NO_MATCHED  PCRE_ERROR_NOMATCH   /* -1 */\n\n#define NGX_REGEX_CASELESS    PCRE_CASELESS\n\n\ntypedef struct {\n    pcre        *code;\n    pcre_extra  *extra;\n} ngx_regex_t;\n\n\ntypedef struct {\n    ngx_str_t     pattern;\n    ngx_pool_t   *pool;\n    ngx_int_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\n#define ngx_regex_exec(re, s, captures, size)                                \\\n    pcre_exec(re->code, re->extra, (const char *) (s)->data, (s)->len, 0, 0, \\\n              captures, size)\n#define ngx_regex_exec_n      \"pcre_exec()\"\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\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\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    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 (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, \"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 (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 = (u_short) -1;\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->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->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            qident = (rn->query[0] << 8) + rn->query[1];\n\n            if (qident == ident) {\n                goto dns_error_name;\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, r->ipv6 ? len * 2 : len);\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 = p + len;\n    }\n#endif\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\\\" 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    /* 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    ngx_memcpy(p, rn->query, rn->qlen);\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#if (NGX_HAVE_INET6)\n    ngx_uint_t                ipv6;                 /* 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\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    ngx_atomic_uint_t  readers;\n\n    readers = *lock;\n\n    if (readers == NGX_RWLOCK_WLOCK) {\n        (void) ngx_atomic_cmp_set(lock, NGX_RWLOCK_WLOCK, 0);\n        return;\n    }\n\n    for ( ;; ) {\n\n        if (ngx_atomic_cmp_set(lock, readers, readers - 1)) {\n            return;\n        }\n\n        readers = *lock;\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_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/* 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 >= 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((const char *) s1, (const char *) 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);\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);\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->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                       &ngx_cycle->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->conn.fd == (ngx_socket_t) -1) {\n        if (ngx_syslog_init_peer(peer) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    /* log syslog socket events with valid log */\n    peer->conn.log = ngx_cycle->log;\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, ngx_cycle->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, ngx_cycle->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, ngx_cycle->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, ngx_cycle->log, ngx_socket_errno,\n                      \"connect() failed\");\n        goto failed;\n    }\n\n    peer->conn.fd = fd;\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, ngx_cycle->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, ngx_cycle->log, ngx_socket_errno,\n                      ngx_close_socket_n \" failed\");\n    }\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_addr_t        server;\n    ngx_connection_t  conn;\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;\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\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\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\")];\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    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\n    ngx_time_update();\n}\n\n\nvoid\nngx_time_update(void)\n{\n    u_char          *p0, *p1, *p2, *p3, *p4;\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\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\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        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\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\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    ngx_memory_barrier();\n\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\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    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\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\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/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    }\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\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    }\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\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\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        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);\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 ((revents & EPOLLIN) && rev->active) {\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 ((revents & EPOLLOUT) && wev->active) {\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\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    }\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\nngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    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 NGX_CONF_ERROR;\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    }\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    }\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_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    }\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    }\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_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\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;\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#endif\n\n\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\n    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\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}\n\n\nngx_int_t\nngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)\n{\n#if (NGX_QUIC)\n\n    ngx_connection_t  *c;\n\n    c = rev->data;\n\n    if (c->quic) {\n        return ngx_quic_handle_read_event(rev, flags);\n    }\n\n#endif\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    c = wev->data;\n\n#if (NGX_QUIC)\n    if (c->quic) {\n        return ngx_quic_handle_write_event(wev, lowat);\n    }\n#endif\n\n    if (lowat) {\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_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    if (!ngx_test_config) {\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\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    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#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    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_queue_init(&ngx_posted_accept_events);\n    ngx_queue_init(&ngx_posted_next_events);\n    ngx_queue_init(&ngx_posted_events);\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    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\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 (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        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            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            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_QUIC)\n    if (c->quic) {\n        return NGX_OK;\n    }\n#endif\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    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, 500);\n\n    return NGX_CONF_OK;\n}\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\nstruct ngx_event_s {\n    void            *data;\n\n    unsigned         write:1;\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\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_AIO_SENDFILE || NGX_COMPAT)\n    ssize_t                  (*preload_handler)(ngx_buf_t *file);\n#endif\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} 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\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#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;\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\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\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);\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\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        ls->handler(c);\n\n        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n            ev->available--;\n        }\n\n    } while (ev->available);\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        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_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\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\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\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\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\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                                     /* 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_session_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 void ngx_ssl_session_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\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_session_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\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_session_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,\n                                                                 NULL, NULL);\n    if (ngx_ssl_session_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    ssl->ctx = SSL_CTX_new(SSLv23_method());\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 (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    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    cert = certs->elts;\n    key = keys->elts;\n\n    for (i = 0; i < certs->nelts; i++) {\n\n        if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\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{\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 (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 (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    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        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#if SSL_CTRL_SET_ECDH_AUTO\n    /* not needed in OpenSSL 1.1.0+ */\n    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\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 (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_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->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#ifdef BIO_get_ktls_send\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        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    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#ifdef BIO_get_ktls_send\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    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    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->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->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->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->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->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 (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\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        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    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 (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\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        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() 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#ifdef BIO_get_ktls_send\n\n    int        sslerr;\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    n = SSL_sendfile(c->ssl->connection, file->file->fd, file->file_pos,\n                     size, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_sendfile: %d\", 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        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        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 (NGX_QUIC)\n    if (c->quic) {\n        /* QUIC streams inherit SSL object */\n        return NGX_OK;\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        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            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        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            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 (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    SSL_free(c->ssl->connection);\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    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_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_NO_SUITABLE_SIGNATURE_ALGORITHM\n            || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM            /*  118 */\n#endif\n            || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG                  /*  129 */\n            || n == SSL_R_DIGEST_CHECK_FAILED                        /*  149 */\n            || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST              /*  151 */\n            || n == SSL_R_EXCESSIVE_MESSAGE_SIZE                     /*  152 */\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_NO_CIPHERS_PASSED\n            || n == SSL_R_NO_CIPHERS_PASSED                          /*  182 */\n#endif\n            || n == SSL_R_NO_CIPHERS_SPECIFIED                       /*  183 */\n            || n == SSL_R_NO_COMPRESSION_SPECIFIED                   /*  187 */\n            || n == SSL_R_NO_SHARED_CIPHER                           /*  193 */\n            || n == SSL_R_RECORD_LENGTH_MISMATCH                     /*  213 */\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_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            || 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            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC        /*  281 */\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_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            || 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    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/TLSv1, typically 32 bytes.\n * It seems that the typical length of the external ASN1 representation\n * of a session is 118 or 119 bytes for SSLv3/TSLv1.\n *\n * Thus on 32-bit platforms we allocate separately an rbtree node,\n * a session id, and an ASN1 representation, they take accordingly\n * 64, 32, and 128 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 128 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, *id, *cached_sess, *session_id;\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    len = i2d_SSL_SESSION(sess, NULL);\n\n    /* do not cache too big session */\n\n    if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {\n        return 0;\n    }\n\n    p = buf;\n    i2d_SSL_SESSION(sess, &p);\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    cached_sess = ngx_slab_alloc_locked(shpool, len);\n\n    if (cached_sess == NULL) {\n\n        /* drop the oldest non-expired session and try once more */\n\n        ngx_ssl_expire_sessions(cache, shpool, 0);\n\n        cached_sess = ngx_slab_alloc_locked(shpool, len);\n\n        if (cached_sess == NULL) {\n            sess_id = NULL;\n            goto failed;\n        }\n    }\n\n    sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));\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, sizeof(ngx_ssl_sess_id_t));\n\n        if (sess_id == NULL) {\n            goto failed;\n        }\n    }\n\n    session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);\n\n#if (NGX_PTR_SIZE == 8)\n\n    id = sess_id->sess_id;\n\n#else\n\n    id = ngx_slab_alloc_locked(shpool, session_id_length);\n\n    if (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        id = ngx_slab_alloc_locked(shpool, session_id_length);\n\n        if (id == NULL) {\n            goto failed;\n        }\n    }\n\n#endif\n\n    ngx_memcpy(cached_sess, buf, len);\n\n    ngx_memcpy(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->id = id;\n    sess_id->len = len;\n    sess_id->session = cached_sess;\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 (cached_sess) {\n        ngx_slab_free_locked(shpool, cached_sess);\n    }\n\n    if (sess_id) {\n        ngx_slab_free_locked(shpool, sess_id);\n    }\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                  \"could not allocate new session%s\", shpool->log_ctx);\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_slab_free_locked(shpool, sess_id->session);\n#if (NGX_PTR_SIZE == 4)\n            ngx_slab_free_locked(shpool, sess_id->id);\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_slab_free_locked(shpool, sess_id->session);\n#if (NGX_PTR_SIZE == 4)\n            ngx_slab_free_locked(shpool, sess_id->id);\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_slab_free_locked(shpool, sess_id->session);\n#if (NGX_PTR_SIZE == 4)\n        ngx_slab_free_locked(shpool, sess_id->id);\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_session_ticket_key_t  *key;\n\n    if (paths == NULL) {\n        return NGX_OK;\n    }\n\n    keys = ngx_array_create(cf->pool, paths->nelts,\n                            sizeof(ngx_ssl_session_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_session_ticket_keys_cleanup;\n    cln->data = keys;\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        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    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_ticket_keys_index, keys)\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    if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx,\n                                         ngx_ssl_session_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    }\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_session_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_session_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#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_session_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 session 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 session 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 session 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        return (i == 0) ? 1 : 2 /* renew */;\n    }\n}\n\n\nstatic void\nngx_ssl_session_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_session_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_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\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    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"
  },
  {
    "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#if (NGX_QUIC)\n#ifdef OPENSSL_IS_BORINGSSL\n#include <openssl/hkdf.h>\n#include <openssl/chacha.h>\n#else\n#include <openssl/kdf.h>\n#endif\n#endif\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};\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    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                    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\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    u_char                     *id;\n    size_t                      len;\n    u_char                     *session;\n    ngx_queue_t                 queue;\n    time_t                      expire;\n#if (NGX_PTR_SIZE == 8)\n    void                       *stub;\n    u_char                      sess_id[32];\n#endif\n};\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_session_cache_t;\n\n\n#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB\n\ntypedef struct {\n    size_t                      size;\n    u_char                      name[16];\n    u_char                      hmac_key[32];\n    u_char                      aes_key[32];\n} ngx_ssl_session_ticket_key_t;\n\n#endif\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\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);\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);\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);\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);\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\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_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\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\nextern int  ngx_ssl_connection_index;\nextern int  ngx_ssl_server_conf_index;\nextern int  ngx_ssl_session_cache_index;\nextern int  ngx_ssl_session_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\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\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\n\n#if (NGX_HAVE_UDP_RECVMMSG)\n#define NGX_UDP_RECVMMSG_MAX  64\n#endif\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_create_udp_connection(ngx_connection_t *c);\nstatic ngx_connection_t *ngx_lookup_udp_connection(ngx_listening_t *ls,\n    ngx_str_t *key, struct sockaddr *local_sockaddr, socklen_t local_socklen);\n\nstatic ngx_int_t ngx_event_process_packet(ngx_event_t *ev, struct msghdr *msg, u_char *buffer, size_t n);\n\nstatic ngx_int_t\nngx_event_process_packet(ngx_event_t *ev, struct msghdr *msg, u_char *buffer, size_t n)\n{\n    size_t             len;\n    ngx_str_t          key;\n    ngx_buf_t          buf;\n    ngx_log_t         *log;\n    socklen_t          local_socklen;\n    ngx_event_t       *rev, *wev;\n    ngx_sockaddr_t     sa, lsa;\n    ngx_udp_dgram_t    dgram;\n    struct sockaddr   *local_sockaddr;\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c, *lc;\n\n#if (NGX_DEBUG)\n    ngx_event_conf_t  *ecf;\n    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);\n#endif\n\n    lc = ev->data;\n    ls = lc->listening;\n\n    dgram.sockaddr = msg->msg_name;\n    dgram.socklen = msg->msg_namelen;\n\n    if (dgram.socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {\n        dgram.socklen = sizeof(ngx_sockaddr_t);\n    }\n\n    if (dgram.socklen == 0) {\n\n        /*\n         * on Linux recvmsg() returns zero msg_namelen\n         * when receiving packets from unbound AF_UNIX sockets\n         */\n\n        ngx_memzero(&sa, sizeof(struct sockaddr));\n        sa.sockaddr.sa_family = ls->sockaddr->sa_family;\n\n        dgram.socklen = sizeof(struct sockaddr);\n        dgram.sockaddr = (struct sockaddr *) &sa;\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    key.data = (u_char *) dgram.sockaddr;\n    key.len = dgram.socklen;\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    if (dgram.sockaddr->sa_family == AF_UNIX) {\n        struct sockaddr_un *saun = (struct sockaddr_un *) dgram.sockaddr;\n\n        if (dgram.socklen <= (socklen_t) offsetof(struct sockaddr_un,\n                                                  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            key.len = 0;\n        }\n    }\n\n#endif\n\n#if (NGX_QUIC)\n    if (ls->quic) {\n        if (ngx_quic_get_packet_dcid(ev->log, buffer, n, &key) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    c = ngx_lookup_udp_connection(ls, &key, local_sockaddr, 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        buf.start = buf.pos;\n        buf.end = buffer + sizeof(buffer);\n\n        rev = c->read;\n\n        dgram.buffer = &buf;\n\n        c->udp->dgram = &dgram;\n\n        rev->ready = 1;\n        rev->active = 0;\n\n        rev->handler(rev);\n\n        if (c->udp) {\n            c->udp->dgram = NULL;\n        }\n\n        rev->ready = 0;\n        rev->active = 1;\n\n        return NGX_OK;\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 NGX_ERROR;\n    }\n\n    c->shared = 1;\n    c->type = SOCK_DGRAM;\n    c->socklen = dgram.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 NGX_ERROR;\n    }\n\n    len = dgram.socklen;\n\n#if (NGX_QUIC)\n    if (ls->quic) {\n        len = NGX_SOCKADDRLEN;\n    }\n#endif\n\n    c->sockaddr = ngx_palloc(c->pool, len);\n    if (c->sockaddr == NULL) {\n        ngx_close_accepted_udp_connection(c);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(c->sockaddr, dgram.sockaddr, dgram.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 NGX_ERROR;\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->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 NGX_ERROR;\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 NGX_ERROR;\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 NGX_ERROR;\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 NGX_ERROR;\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_create_udp_connection(c) != NGX_OK) {\n        ngx_close_accepted_udp_connection(c);\n        return NGX_ERROR;\n    }\n\n    log->data = NULL;\n    log->handler = NULL;\n\n    ls->handler(c);\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_UDP_RECVMMSG)\n\nvoid\nngx_event_recvmsg(ngx_event_t *ev)\n{\n    ssize_t            n;\n    ngx_err_t          err;\n    struct iovec       iov[NGX_UDP_RECVMMSG_MAX];\n    struct mmsghdr     msgs[NGX_UDP_RECVMMSG_MAX];\n    ngx_sockaddr_t     sa[NGX_UDP_RECVMMSG_MAX];\n    ngx_listening_t   *ls;\n    ngx_event_conf_t  *ecf;\n    ngx_connection_t  *lc;\n    static u_char      buffer[NGX_UDP_RECVMMSG_MAX][65535];\n    ngx_int_t          i;\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n    u_char             msg_control[NGX_UDP_RECVMMSG_MAX][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(msgs, sizeof(msgs));\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n        ngx_memzero(msg_control, sizeof(msg_control));\n#endif\n\n        for (i = 0; i < NGX_UDP_RECVMMSG_MAX; i++) {\n            iov[i].iov_base = buffer[i];\n            iov[i].iov_len = sizeof(buffer[i]);\n\n            msgs[i].msg_hdr.msg_name = &sa[i];\n            msgs[i].msg_hdr.msg_namelen = sizeof(ngx_sockaddr_t);\n\n            msgs[i].msg_hdr.msg_iov = &iov[i];\n            msgs[i].msg_hdr.msg_iovlen = 1;\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n            if (ls->wildcard) {\n                msgs[i].msg_hdr.msg_control = &msg_control[i];\n                msgs[i].msg_hdr.msg_controllen = sizeof(msg_control[i]);\n           }\n#endif\n        }\n\n        n = recvmmsg(lc->fd, msgs, NGX_UDP_RECVMMSG_MAX, 0, NULL);\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        for (i = 0; i < n; i++) {\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n            if (msgs[i].msg_hdr.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            (void) ngx_event_process_packet(ev, &msgs[i].msg_hdr, buffer[i], msgs[i].msg_len);\n\n            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                ev->available -= n;\n            }\n        }\n\n    } while (ev->available);\n}\n\n#else\n\nvoid\nngx_event_recvmsg(ngx_event_t *ev)\n{\n    ssize_t            n;\n    ngx_err_t          err;\n    struct iovec       iov[1];\n    struct msghdr      msg;\n    ngx_sockaddr_t     sa;\n    ngx_listening_t   *ls;\n    ngx_event_conf_t  *ecf;\n    ngx_connection_t  *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        (void) ngx_event_process_packet(ev, &msg, buffer, n);\n\n        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n            ev->available -= n;\n        }\n\n    } while (ev->available);\n}\n\n#endif\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->dgram == NULL) {\n        return NGX_AGAIN;\n    }\n\n    b = c->udp->dgram->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->dgram = 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_memn2cmp(udp->key.data, udpt->key.data,\n                              udp->key.len, udpt->key.len);\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_create_udp_connection(ngx_connection_t *c)\n{\n    ngx_str_t              key;\n    ngx_pool_cleanup_t    *cln;\n    ngx_udp_connection_t  *udp;\n\n#if (NGX_QUIC)\n    if (c->listening->quic) {\n        return NGX_OK;\n    }\n#endif\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    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    key.data = (u_char *) c->sockaddr;\n    key.len = c->socklen;\n\n    ngx_insert_udp_connection(c, udp, &key);\n\n    c->udp = udp;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_insert_udp_connection(ngx_connection_t *c, ngx_udp_connection_t *udp,\n    ngx_str_t *key)\n{\n    uint32_t  hash;\n\n    ngx_crc32_init(hash);\n\n    ngx_crc32_update(&hash, key->data, key->len);\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->connection = c;\n    udp->key = *key;\n    udp->node.key = hash;\n\n    ngx_rbtree_insert(&c->listening->rbtree, &udp->node);\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, ngx_str_t *key,\n    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 (key->len == 0) {\n        return NULL;\n    }\n\n    node = ls->rbtree.root;\n    sentinel = ls->rbtree.sentinel;\n\n    ngx_crc32_init(hash);\n    ngx_crc32_update(&hash, key->data, key->len);\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_memn2cmp(key->data, udp->key.data, key->len, udp->key.len);\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\n#if (NGX_QUIC)\n            if (ls->quic && c->udp != udp) {\n                c->udp = udp;\n            }\n#endif\n\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#endif\n\n\ntypedef struct {\n    ngx_buf_t                 *buffer;\n    struct sockaddr           *sockaddr;\n    socklen_t                  socklen;\n} ngx_udp_dgram_t;\n\n\nstruct ngx_udp_connection_s {\n    ngx_rbtree_node_t          node;\n    ngx_connection_t          *connection;\n    ngx_str_t                  key;\n    ngx_udp_dgram_t           *dgram;\n};\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\n\nvoid ngx_event_recvmsg(ngx_event_t *ev);\nssize_t ngx_sendmsg(ngx_connection_t *c, struct msghdr *msg, int flags);\n#if (NGX_HAVE_UDP_SENDMMSG)\nngx_int_t\nngx_sendmmsg(ngx_connection_t *c, struct mmsghdr *msgs, ngx_int_t count, int flags);\n#endif\nvoid ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nvoid ngx_insert_udp_connection(ngx_connection_t *c, ngx_udp_connection_t *udp,\n    ngx_str_t *key);\n\n#endif\n\nvoid ngx_delete_udp_connection(void *data);\n\n\n#endif /* _NGX_EVENT_UDP_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/bpf/bpfgen.sh",
    "content": "#!/bin/bash\n\nexport LANG=C\n\nset -e\n\nif [ $# -lt 1 ]; then\n    echo \"Usage: PROGNAME=foo LICENSE=bar $0 <bpf object file>\"\n    exit 1\nfi\n\n\nself=$0\nfilename=$1\nfuncname=$PROGNAME\n\ngenerate_head()\n{\n    cat << END\n/* AUTO-GENERATED, DO NOT EDIT. */\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"ngx_bpf.h\"\n\n\nEND\n}\n\ngenerate_tail()\n{\n    cat << END\n\nngx_bpf_program_t $PROGNAME = {\n    .relocs = bpf_reloc_prog_$funcname,\n    .nrelocs = sizeof(bpf_reloc_prog_$funcname)\n               / sizeof(bpf_reloc_prog_$funcname[0]),\n    .ins = bpf_insn_prog_$funcname,\n    .nins = sizeof(bpf_insn_prog_$funcname)\n            / sizeof(bpf_insn_prog_$funcname[0]),\n    .license = \"$LICENSE\",\n    .type = BPF_PROG_TYPE_SK_REUSEPORT,\n};\n\nEND\n}\n\nprocess_relocations()\n{\n    echo \"static ngx_bpf_reloc_t bpf_reloc_prog_$funcname[] = {\"\n\n    objdump -r $filename | awk '{\n\n    if (enabled && $NF > 0) {\n        off = strtonum(sprintf(\"0x%s\", $1));\n        name = $3;\n\n        printf(\"    { \\\"%s\\\", %d },\\n\", name, off/8);\n    }\n\n    if ($1 == \"OFFSET\") {\n        enabled=1;\n    }\n}'\n    echo \"};\"\n    echo\n}\n\nprocess_section()\n{\n    echo \"static struct bpf_insn bpf_insn_prog_$funcname[] = {\"\n    echo \"    /* opcode dst          src         offset imm */\"\n\n    section_info=$(objdump -h $filename --section=$funcname | grep \"1 $funcname\")\n\n    # dd doesn't know hex\n    length=$(printf \"%d\" 0x$(echo $section_info | cut -d ' ' -f3))\n    offset=$(printf \"%d\" 0x$(echo $section_info | cut -d ' ' -f6))\n\n    for ins in $(dd if=\"$filename\" bs=1 count=$length skip=$offset status=none | xxd -p -c 8)\n    do\n        opcode=0x${ins:0:2}\n        srcdst=0x${ins:2:2}\n\n        # bytes are dumped in LE order\n        offset=0x${ins:6:2}${ins:4:2}                        # short\n        immedi=0x${ins:14:2}${ins:12:2}${ins:10:2}${ins:8:2} # int\n\n        dst=\"$(($srcdst & 0xF))\"\n        src=\"$(($srcdst & 0xF0))\"\n        src=\"$(($src >> 4))\"\n\n        opcode=$(printf \"0x%x\" $opcode)\n        dst=$(printf \"BPF_REG_%d\" $dst)\n        src=$(printf \"BPF_REG_%d\" $src)\n        offset=$(printf \"%d\" $offset)\n        immedi=$(printf \"0x%x\" $immedi)\n\n        printf \"    { %4s, %11s, %11s, (int16_t) %6s, %10s },\\n\" $opcode $dst $src $offset $immedi\n    done\n\ncat << END\n};\n\nEND\n}\n\ngenerate_head\nprocess_relocations\nprocess_section\ngenerate_tail\n\n"
  },
  {
    "path": "src/event/quic/bpf/makefile",
    "content": "CFLAGS=-O2 -Wall\n\nLICENSE=BSD\n\nPROGNAME=ngx_quic_reuseport_helper\nRESULT=ngx_event_quic_bpf_code\nDEST=../$(RESULT).c\n\nall: $(RESULT)\n\n$(RESULT): $(PROGNAME).o\n\tLICENSE=$(LICENSE) PROGNAME=$(PROGNAME) bash ./bpfgen.sh $< > $@\n\nDEFS=-DPROGNAME=\\\"$(PROGNAME)\\\"                                               \\\n     -DLICENSE_$(LICENSE)                                                     \\\n     -DLICENSE=\\\"$(LICENSE)\\\"                                                 \\\n\n$(PROGNAME).o: $(PROGNAME).c\n\tclang $(CFLAGS) $(DEFS) -target bpf -c $< -o $@\n\ninstall: $(RESULT)\n\tcp $(RESULT) $(DEST)\n\nclean:\n\t@rm -f $(RESULT) *.o\n\ndebug: $(PROGNAME).o\n\tllvm-objdump -S -no-show-raw-insn $<\n\n.DELETE_ON_ERROR:\n"
  },
  {
    "path": "src/event/quic/bpf/ngx_quic_reuseport_helper.c",
    "content": "#include <errno.h>\n#include <linux/string.h>\n#include <linux/udp.h>\n#include <linux/bpf.h>\n/*\n * the bpf_helpers.h is not included into linux-headers, only available\n * with kernel sources in \"tools/lib/bpf/bpf_helpers.h\" or in libbpf.\n */\n#include <bpf/bpf_helpers.h>\n\n\n#if !defined(SEC)\n#define SEC(NAME)  __attribute__((section(NAME), used))\n#endif\n\n\n#if defined(LICENSE_GPL)\n\n/*\n * To see debug:\n *\n *  echo 1 > /sys/kernel/debug/tracing/events/bpf_trace/enable\n *  cat /sys/kernel/debug/tracing/trace_pipe\n *  echo 0 > /sys/kernel/debug/tracing/events/bpf_trace/enable\n */\n\n#define debugmsg(fmt, ...)                                                    \\\ndo {                                                                          \\\n    char __buf[] = fmt;                                                       \\\n    bpf_trace_printk(__buf, sizeof(__buf), ##__VA_ARGS__);                    \\\n} while (0)\n\n#else\n\n#define debugmsg(fmt, ...)\n\n#endif\n\nchar _license[] SEC(\"license\") = LICENSE;\n\n/*****************************************************************************/\n\n#define NGX_QUIC_PKT_LONG              0x80  /* header form */\n#define NGX_QUIC_SERVER_CID_LEN        20\n#define NGX_QUIC_BPF_ACTIVE_KEYS_MAX   256\n\n\n#define advance_data(nbytes)                                                  \\\n    offset += nbytes;                                                         \\\n    if (start + offset > end) {                                               \\\n        debugmsg(\"cannot read %ld bytes at offset %ld\", nbytes, offset);      \\\n        goto failed;                                                          \\\n    }                                                                         \\\n    data = start + offset - 1;\n\n\n#define ngx_quic_parse_uint64(p)                                              \\\n    (((__u64)(p)[0] << 56) |                                                  \\\n     ((__u64)(p)[1] << 48) |                                                  \\\n     ((__u64)(p)[2] << 40) |                                                  \\\n     ((__u64)(p)[3] << 32) |                                                  \\\n     ((__u64)(p)[4] << 24) |                                                  \\\n     ((__u64)(p)[5] << 16) |                                                  \\\n     ((__u64)(p)[6] << 8)  |                                                  \\\n     ((__u64)(p)[7]))\n\n/*\n * actual map object is created by the \"bpf\" system call,\n * all pointers to this variable are replaced by the bpf loader\n */\nstruct bpf_map_def SEC(\"maps\") ngx_quic_sockmap;\nstruct bpf_map_def SEC(\"maps\") ngx_quic_sockmap_active;\n\n\nSEC(PROGNAME)\nint ngx_quic_select_socket_by_dcid(struct sk_reuseport_md *ctx)\n{\n    int             rc;\n    __u64           key;\n    size_t          len, offset;\n    unsigned char  *start, *end, *data, *dcid;\n\n    start = ctx->data;\n    end = (unsigned char *) ctx->data_end;\n    offset = 0;\n\n    advance_data(sizeof(struct udphdr)); /* data at UDP header */\n    advance_data(1); /* data at QUIC flags */\n\n    if (data[0] & NGX_QUIC_PKT_LONG) {\n\n        advance_data(4); /* data at QUIC version */\n        advance_data(1); /* data at DCID len */\n\n        len = data[0];   /* read DCID length */\n\n        if (len < 8) {\n            /* it's useless to search for key in such short DCID */\n            goto failed;\n        }\n\n    } else {\n        len = NGX_QUIC_SERVER_CID_LEN;\n    }\n\n    dcid = &data[1];\n    advance_data(len); /* we expect the packet to have full DCID */\n\n    /* make verifier happy */\n    if (dcid + sizeof(__u64) > end) {\n        goto failed;\n    }\n\n    key = ngx_quic_parse_uint64(dcid);\n\n    rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap, &key, 0);\n\n    switch (rc) {\n    case 0:\n        debugmsg(\"nginx quic socket selected by key 0x%llx\", key);\n        return SK_PASS;\n\n    /* kernel returns positive error numbers, errno.h defines positive */\n    case -ENOENT:\n        debugmsg(\"nginx quic default route for key 0x%llx\", key);\n        /* let the default reuseport logic decide which socket to choose */\n        goto failed;\n\n    default:\n        debugmsg(\"nginx quic bpf_sk_select_reuseport err: %d key 0x%llx\",\n                  rc, key);\n        goto failed;\n    }\n\nfailed:\n    key = ctx->hash % NGX_QUIC_BPF_ACTIVE_KEYS_MAX;\n    rc = bpf_sk_select_reuseport(ctx, &ngx_quic_sockmap_active, &key, 0);\n\n    switch (rc) {\n    case 0:\n        debugmsg(\"nginx quic socket selected active socket\");\n        break;\n\n    case -ENOENT:\n        debugmsg(\"nginx quic default route (failed with active socket)\");\n        break;\n\n    default:\n        debugmsg(\"nginx quic bpf_sk_select_reuseport err active socket: %d\", rc);\n        break;\n    }\n\n    /*\n     * SK_DROP will generate ICMP, but we may want to process \"invalid\" packet\n     * in userspace quic to investigate further and finally react properly\n     * (maybe ignore, maybe send something in response or close connection)\n     */\n    return SK_PASS;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic.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#include <ngx_event_quic_connection.h>\n\n\nstatic ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c,\n    ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);\nstatic ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,\n    ngx_quic_header_t *pkt);\nstatic void ngx_quic_input_handler(ngx_event_t *rev);\n\nstatic ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc);\nstatic void ngx_quic_close_timer_handler(ngx_event_t *ev);\n\nstatic ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b,\n    ngx_quic_conf_t *conf);\nstatic ngx_int_t ngx_quic_process_packet(ngx_connection_t *c,\n    ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);\nstatic ngx_int_t ngx_quic_process_payload(ngx_connection_t *c,\n    ngx_quic_header_t *pkt);\nstatic ngx_int_t ngx_quic_check_csid(ngx_quic_connection_t *qc,\n    ngx_quic_header_t *pkt);\nstatic ngx_int_t ngx_quic_handle_frames(ngx_connection_t *c,\n    ngx_quic_header_t *pkt);\n\nstatic void ngx_quic_push_handler(ngx_event_t *ev);\n\n\nstatic ngx_core_module_t  ngx_quic_module_ctx = {\n    ngx_string(\"quic\"),\n    NULL,\n    NULL\n};\n\n\nngx_module_t  ngx_quic_module = {\n    NGX_MODULE_V1,\n    &ngx_quic_module_ctx,                  /* module context */\n    NULL,                                  /* 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\n#if (NGX_DEBUG)\n\nvoid\nngx_quic_connstate_dbg(ngx_connection_t *c)\n{\n    u_char                 *p, *last;\n    ngx_quic_connection_t  *qc;\n    u_char                  buf[NGX_MAX_ERROR_STR];\n\n    p = buf;\n    last = p + sizeof(buf);\n\n    qc = ngx_quic_get_connection(c);\n\n    p = ngx_slprintf(p, last, \"state:\");\n\n    if (qc) {\n\n        if (qc->error) {\n            p = ngx_slprintf(p, last, \"%s\", qc->error_app ? \" app\" : \"\");\n            p = ngx_slprintf(p, last, \" error:%ui\", qc->error);\n\n            if (qc->error_reason) {\n                p = ngx_slprintf(p, last, \" \\\"%s\\\"\", qc->error_reason);\n            }\n        }\n\n        p = ngx_slprintf(p, last, \"%s\", qc->shutdown ? \" shutdown\" : \"\");\n        p = ngx_slprintf(p, last, \"%s\", qc->closing ? \" closing\" : \"\");\n        p = ngx_slprintf(p, last, \"%s\", qc->draining ? \" draining\" : \"\");\n        p = ngx_slprintf(p, last, \"%s\", qc->key_phase ? \" kp\" : \"\");\n\n    } else {\n        p = ngx_slprintf(p, last, \" early\");\n    }\n\n    if (c->read->timer_set) {\n        p = ngx_slprintf(p, last,\n                         qc && qc->send_timer_set ? \" send:%M\" : \" read:%M\",\n                         c->read->timer.key - ngx_current_msec);\n    }\n\n    if (qc) {\n\n        if (qc->push.timer_set) {\n            p = ngx_slprintf(p, last, \" push:%M\",\n                             qc->push.timer.key - ngx_current_msec);\n        }\n\n        if (qc->pto.timer_set) {\n            p = ngx_slprintf(p, last, \" pto:%M\",\n                             qc->pto.timer.key - ngx_current_msec);\n        }\n\n        if (qc->close.timer_set) {\n            p = ngx_slprintf(p, last, \" close:%M\",\n                             qc->close.timer.key - ngx_current_msec);\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic %*s\", p - buf, buf);\n}\n\n#endif\n\n\nngx_int_t\nngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)\n{\n    ngx_str_t               scid;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    scid.data = qc->socket->cid->id;\n    scid.len = qc->socket->cid->len;\n\n    if (scid.len != ctp->initial_scid.len\n        || ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)\n    {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic client initial_source_connection_id mismatch\");\n        return NGX_ERROR;\n    }\n\n    if (ctp->max_udp_payload_size < NGX_QUIC_MIN_INITIAL_SIZE\n        || ctp->max_udp_payload_size > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE)\n    {\n        qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;\n        qc->error_reason = \"invalid maximum packet size\";\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic maximum packet size is invalid\");\n        return NGX_ERROR;\n\n    } else if (ctp->max_udp_payload_size > ngx_quic_max_udp_payload(c)) {\n        ctp->max_udp_payload_size = ngx_quic_max_udp_payload(c);\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic client maximum packet size truncated\");\n    }\n\n    if (ctp->active_connection_id_limit < 2) {\n        qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;\n        qc->error_reason = \"invalid active_connection_id_limit\";\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic active_connection_id_limit is invalid\");\n        return NGX_ERROR;\n    }\n\n    if (ctp->ack_delay_exponent > 20) {\n        qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;\n        qc->error_reason = \"invalid ack_delay_exponent\";\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic ack_delay_exponent is invalid\");\n        return NGX_ERROR;\n    }\n\n    if (ctp->max_ack_delay >= 16384) {\n        qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;\n        qc->error_reason = \"invalid max_ack_delay\";\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic max_ack_delay is invalid\");\n        return NGX_ERROR;\n    }\n\n    if (ctp->max_idle_timeout > 0\n        && ctp->max_idle_timeout < qc->tp.max_idle_timeout)\n    {\n        qc->tp.max_idle_timeout = ctp->max_idle_timeout;\n    }\n\n    qc->streams.server_max_streams_bidi = ctp->initial_max_streams_bidi;\n    qc->streams.server_max_streams_uni = ctp->initial_max_streams_uni;\n\n    ngx_memcpy(&qc->ctp, ctp, sizeof(ngx_quic_tp_t));\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf)\n{\n    ngx_int_t               rc;\n    ngx_quic_connection_t  *qc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic run\");\n\n    rc = ngx_quic_input(c, c->buffer, conf);\n    if (rc != NGX_OK) {\n        ngx_quic_close_connection(c, rc == NGX_DECLINED ? NGX_DONE : NGX_ERROR);\n        return;\n    }\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc == NULL) {\n        ngx_quic_close_connection(c, NGX_DONE);\n        return;\n    }\n\n    ngx_add_timer(c->read, qc->tp.max_idle_timeout);\n    ngx_quic_connstate_dbg(c);\n\n    c->read->handler = ngx_quic_input_handler;\n\n    return;\n}\n\n\nstatic ngx_quic_connection_t *\nngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,\n    ngx_quic_header_t *pkt)\n{\n    ngx_uint_t              i;\n    ngx_quic_tp_t          *ctp;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_pcalloc(c->pool, sizeof(ngx_quic_connection_t));\n    if (qc == NULL) {\n        return NULL;\n    }\n\n    qc->keys = ngx_quic_keys_new(c->pool);\n    if (qc->keys == NULL) {\n        return NULL;\n    }\n\n    qc->version = pkt->version;\n\n    ngx_rbtree_init(&qc->streams.tree, &qc->streams.sentinel,\n                    ngx_quic_rbtree_insert_stream);\n\n    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n        ngx_queue_init(&qc->send_ctx[i].frames);\n        ngx_queue_init(&qc->send_ctx[i].sending);\n        ngx_queue_init(&qc->send_ctx[i].sent);\n        qc->send_ctx[i].largest_pn = NGX_QUIC_UNSET_PN;\n        qc->send_ctx[i].largest_ack = NGX_QUIC_UNSET_PN;\n        qc->send_ctx[i].largest_range = NGX_QUIC_UNSET_PN;\n        qc->send_ctx[i].pending_ack = NGX_QUIC_UNSET_PN;\n\n        ngx_queue_init(&qc->send_ctx[i].fqueues);\n        qc->send_ctx[i].fqueue.frames = &qc->send_ctx[i].frames;\n    }\n\n    qc->send_ctx[0].level = ssl_encryption_initial;\n    qc->send_ctx[1].level = ssl_encryption_handshake;\n    qc->send_ctx[2].level = ssl_encryption_application;\n\n    ngx_queue_init(&qc->free_frames);\n\n    qc->avg_rtt = NGX_QUIC_INITIAL_RTT;\n    qc->rttvar = NGX_QUIC_INITIAL_RTT / 2;\n    qc->min_rtt = NGX_TIMER_INFINITE;\n    qc->first_rtt = NGX_TIMER_INFINITE;\n\n    /*\n     * qc->latest_rtt = 0\n     */\n\n    qc->pto.log = c->log;\n    qc->pto.data = c;\n    qc->pto.handler = ngx_quic_pto_handler;\n    qc->pto.cancelable = 1;\n\n    qc->push.log = c->log;\n    qc->push.data = c;\n    qc->push.handler = ngx_quic_push_handler;\n    qc->push.cancelable = 1;\n\n    qc->path_validation.log = c->log;\n    qc->path_validation.data = c;\n    qc->path_validation.handler = ngx_quic_path_validation_handler;\n    qc->path_validation.cancelable = 1;\n\n    qc->conf = conf;\n    qc->tp = conf->tp;\n\n    ctp = &qc->ctp;\n\n    /* defaults to be used before actual client parameters are received */\n    ctp->max_udp_payload_size = ngx_quic_max_udp_payload(c);\n    ctp->ack_delay_exponent = NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT;\n    ctp->max_ack_delay = NGX_QUIC_DEFAULT_MAX_ACK_DELAY;\n    ctp->active_connection_id_limit = 2;\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    qc->mtu.min_probe_length = NGX_QUIC_MIN_INITIAL_SIZE;\n    qc->mtu.max_probe_length = qc->conf->mtu_target;\n    qc->mtu.remaining_probe_count = qc->conf->mtu_attemts;\n    qc->mtu.packets_between_probes = 100;\n    qc->mtu.next_probe_at = qc->mtu.packets_between_probes;\n#endif\n\n    ngx_queue_init(&qc->streams.uninitialized);\n\n    qc->streams.recv_max_data = qc->tp.initial_max_data;\n    qc->streams.recv_window = qc->streams.recv_max_data;\n\n    qc->streams.client_max_streams_uni = qc->tp.initial_max_streams_uni;\n    qc->streams.client_max_streams_bidi = qc->tp.initial_max_streams_bidi;\n\n    if (conf->initial_window) {\n        qc->congestion.window = conf->initial_window;\n    } else {\n        qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,\n                                    ngx_max(2 * qc->tp.max_udp_payload_size,\n                                            14720));\n    }\n\n    qc->congestion.ssthresh = (size_t) -1;\n    qc->congestion.recovery_start = ngx_current_msec;\n\n    if (pkt->validated && pkt->retried) {\n        qc->tp.retry_scid.len = pkt->dcid.len;\n        qc->tp.retry_scid.data = ngx_pstrdup(c->pool, &pkt->dcid);\n        if (qc->tp.retry_scid.data == NULL) {\n            return NULL;\n        }\n    }\n\n    if (ngx_quic_keys_set_initial_secret(c->pool, qc->keys, &pkt->dcid,\n                                         qc->version)\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    qc->validated = pkt->validated;\n\n    if (ngx_quic_open_sockets(c, qc, pkt) != NGX_OK) {\n        return NULL;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic connection created\");\n\n    return qc;\n}\n\n\nstatic ngx_int_t\nngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)\n{\n    u_char                 *tail, ch;\n    ngx_uint_t              i;\n    ngx_queue_t            *q;\n    ngx_quic_client_id_t   *cid;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    /* A stateless reset uses an entire UDP datagram */\n    if (!pkt->first) {\n        return NGX_DECLINED;\n    }\n\n    tail = pkt->raw->last - NGX_QUIC_SR_TOKEN_LEN;\n\n    for (q = ngx_queue_head(&qc->client_ids);\n         q != ngx_queue_sentinel(&qc->client_ids);\n         q = ngx_queue_next(q))\n    {\n        cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);\n\n        if (cid->seqnum == 0 || cid->refcnt == 0) {\n            /*\n             * No stateless reset token in initial connection id.\n             * Don't accept a token from an unused connection id.\n             */\n            continue;\n        }\n\n        /* constant time comparison */\n\n        for (ch = 0, i = 0; i < NGX_QUIC_SR_TOKEN_LEN; i++) {\n            ch |= tail[i] ^ cid->sr_token[i];\n        }\n\n        if (ch == 0) {\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_quic_input_handler(ngx_event_t *rev)\n{\n    ngx_int_t               rc;\n    ngx_buf_t              *b;\n    ngx_connection_t       *c;\n    ngx_quic_connection_t  *qc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, \"quic input handler\");\n\n    c = rev->data;\n    qc = ngx_quic_get_connection(c);\n\n    c->log->action = \"handling quic input\";\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                      \"quic client timed out\");\n        ngx_quic_close_connection(c, NGX_DONE);\n        return;\n    }\n\n    if (c->close) {\n        qc->error_reason = \"graceful shutdown\";\n        ngx_quic_close_connection(c, NGX_OK);\n        return;\n    }\n\n    if (!rev->ready) {\n        if (qc->closing) {\n            ngx_quic_close_connection(c, NGX_OK);\n\n        } else if (qc->shutdown) {\n            ngx_quic_shutdown_quic(c, qc->shutdown_rc);\n        }\n\n        return;\n    }\n\n    b = c->udp->dgram->buffer;\n\n    rc = ngx_quic_input(c, b, NULL);\n\n    if (rc == NGX_ERROR) {\n        ngx_quic_close_connection(c, NGX_ERROR);\n        return;\n    }\n\n    if (rc == NGX_DECLINED) {\n        return;\n    }\n\n    /* rc == NGX_OK */\n\n    qc->send_timer_set = 0;\n    ngx_add_timer(rev, qc->tp.max_idle_timeout);\n\n    ngx_quic_connstate_dbg(c);\n}\n\n\nvoid\nngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc)\n{\n    ngx_pool_t             *pool;\n    ngx_quic_connection_t  *qc;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_close_connection rc:%i\", rc);\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc == NULL) {\n        if (rc == NGX_ERROR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic close connection early error\");\n        }\n\n    } else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) {\n        return;\n    }\n\n    if (c->ssl) {\n        (void) ngx_ssl_shutdown(c);\n    }\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_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 ngx_int_t\nngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc)\n{\n    ngx_uint_t              i;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (!qc->closing) {\n\n        /* drop packets from retransmit queues, no ack is expected */\n        for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n            ngx_quic_free_frames(c, &qc->send_ctx[i].sent);\n        }\n\n        if (rc == NGX_DONE) {\n\n            /*\n             * RFC 9000, 10.1.  Idle Timeout\n             *\n             *  If a max_idle_timeout is specified by either endpoint in its\n             *  transport parameters (Section 18.2), the connection is silently\n             *  closed and its state is discarded when it remains idle\n             */\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic closing %s connection\",\n                           qc->draining ? \"drained\" : \"idle\");\n\n        } else {\n\n            /*\n             * RFC 9000, 10.2.  Immediate Close\n             *\n             *  An endpoint sends a CONNECTION_CLOSE frame (Section 19.19)\n             *  to terminate the connection immediately.\n             */\n\n            qc->error_level = c->ssl ? SSL_quic_read_level(c->ssl->connection)\n                                     : ssl_encryption_initial;\n\n            if (rc == NGX_OK) {\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"quic immediate close drain:%d\",\n                               qc->draining);\n\n                qc->close.log = c->log;\n                qc->close.data = c;\n                qc->close.handler = ngx_quic_close_timer_handler;\n                qc->close.cancelable = 1;\n\n                ctx = ngx_quic_get_send_ctx(qc, qc->error_level);\n\n                ngx_add_timer(&qc->close, 3 * ngx_quic_pto(c, ctx));\n\n                qc->error = NGX_QUIC_ERR_NO_ERROR;\n\n            } else {\n                if (qc->error == 0 && !qc->error_app) {\n                    qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;\n                }\n\n                ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"quic immediate close due to %s error: %ui %s\",\n                               qc->error_app ? \"app \" : \"\", qc->error,\n                               qc->error_reason ? qc->error_reason : \"\");\n            }\n\n            (void) ngx_quic_send_cc(c);\n\n            if (qc->error_level == ssl_encryption_handshake) {\n                /* for clients that might not have handshake keys */\n                qc->error_level = ssl_encryption_initial;\n                (void) ngx_quic_send_cc(c);\n            }\n        }\n\n        qc->closing = 1;\n    }\n\n    if (rc == NGX_ERROR && qc->close.timer_set) {\n        /* do not wait for timer in case of fatal error */\n        ngx_del_timer(&qc->close);\n    }\n\n    if (ngx_quic_close_streams(c, qc) == NGX_AGAIN) {\n        return NGX_AGAIN;\n    }\n\n    if (qc->push.timer_set) {\n        ngx_del_timer(&qc->push);\n    }\n\n    if (qc->pto.timer_set) {\n        ngx_del_timer(&qc->pto);\n    }\n\n    if (qc->path_validation.timer_set) {\n        ngx_del_timer(&qc->path_validation);\n    }\n\n    if (qc->push.posted) {\n        ngx_delete_posted_event(&qc->push);\n    }\n\n    if (qc->close.timer_set) {\n        return NGX_AGAIN;\n    }\n\n    ngx_quic_close_sockets(c);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic part of connection is terminated\");\n\n    /* may be tested from SSL callback during SSL shutdown */\n    c->udp = NULL;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_finalize_connection(ngx_connection_t *c, ngx_int_t rc, ngx_uint_t err,\n    const char *reason)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    qc->error = err;\n    qc->error_reason = reason;\n    qc->error_app = 1;\n    qc->error_ftype = 0;\n\n    ngx_quic_close_connection(c, rc);\n}\n\n\nvoid\nngx_quic_shutdown_connection(ngx_connection_t *c, ngx_int_t rc, ngx_uint_t err,\n    const char *reason)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    qc->shutdown = 1;\n    qc->shutdown_rc = rc;\n    qc->shutdown_code = err;\n    qc->shutdown_reason = reason;\n\n    ngx_quic_shutdown_quic(c, rc);\n}\n\n\nstatic void\nngx_quic_close_timer_handler(ngx_event_t *ev)\n{\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"quic close timer\");\n\n    c = ev->data;\n    ngx_quic_close_connection(c, NGX_DONE);\n}\n\n\nstatic ngx_int_t\nngx_quic_input(ngx_connection_t *c, ngx_buf_t *b, ngx_quic_conf_t *conf)\n{\n    size_t                  size;\n    u_char                 *p, *start;\n    ngx_int_t               rc;\n    ngx_uint_t              good;\n    ngx_quic_header_t       pkt;\n    ngx_quic_connection_t  *qc;\n\n    good = 0;\n\n    size = b->last - b->pos;\n\n    p = start = b->pos;\n\n    while (p < b->last) {\n\n        ngx_memzero(&pkt, sizeof(ngx_quic_header_t));\n        pkt.raw = b;\n        pkt.data = p;\n        pkt.len = b->last - p;\n        pkt.log = c->log;\n        pkt.first = (p == start) ? 1 : 0;\n        pkt.flags = p[0];\n        pkt.raw->pos++;\n\n        rc = ngx_quic_process_packet(c, conf, &pkt);\n\n#if (NGX_DEBUG)\n        if (pkt.parsed) {\n            ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic packet %s done decr:%d pn:%L perr:%ui rc:%i\",\n                           ngx_quic_level_name(pkt.level), pkt.decrypted,\n                           pkt.pn, pkt.error, rc);\n        } else {\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic packet done parse failed rc:%i\", rc);\n        }\n#endif\n\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        if (rc == NGX_DONE) {\n            /* stop further processing */\n            return NGX_DECLINED;\n        }\n\n        if (rc == NGX_OK) {\n            good = 1;\n        }\n\n        /* NGX_OK || NGX_DECLINED */\n\n        /*\n         * we get NGX_DECLINED when there are no keys [yet] available\n         * to decrypt packet.\n         * Instead of queueing it, we ignore it and rely on the sender's\n         * retransmission:\n         *\n         * RFC 9000, 12.2.  Coalescing Packets\n         *\n         * For example, if decryption fails (because the keys are\n         * not available or for any other reason), the receiver MAY either\n         * discard or buffer the packet for later processing and MUST\n         * attempt to process the remaining packets.\n         *\n         * We also skip packets that don't match connection state\n         * or cannot be parsed properly.\n         */\n\n        /* b->pos is at header end, adjust by actual packet length */\n        b->pos = pkt.data + pkt.len;\n\n        p = b->pos;\n    }\n\n    if (!good) {\n        return NGX_DECLINED;\n    }\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc) {\n        qc->received += size;\n\n        if ((uint64_t) (c->sent + qc->received) / 8 >\n            (qc->streams.sent + qc->streams.recv_last) + 1048576)\n        {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0, \"quic flood detected\");\n\n            qc->error = NGX_QUIC_ERR_NO_ERROR;\n            qc->error_reason = \"QUIC flood detected\";\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_process_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,\n    ngx_quic_header_t *pkt)\n{\n    ngx_int_t               rc;\n    ngx_quic_connection_t  *qc;\n\n    c->log->action = \"parsing quic packet\";\n\n    rc = ngx_quic_parse_packet(pkt);\n\n    if (rc == NGX_DECLINED || rc == NGX_ERROR) {\n        return rc;\n    }\n\n    pkt->parsed = 1;\n\n    c->log->action = \"processing quic packet\";\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic packet rx dcid len:%uz %xV\",\n                   pkt->dcid.len, &pkt->dcid);\n\n#if (NGX_DEBUG)\n    if (pkt->level != ssl_encryption_application) {\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic packet rx scid len:%uz %xV\",\n                       pkt->scid.len, &pkt->scid);\n    }\n\n    if (pkt->level == ssl_encryption_initial) {\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic address validation token len:%uz %xV\",\n                       pkt->token.len, &pkt->token);\n    }\n#endif\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc) {\n\n        if (rc == NGX_ABORT) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"quic unsupported version: 0x%xD\", pkt->version);\n            return NGX_DECLINED;\n        }\n\n        if (pkt->level != ssl_encryption_application) {\n\n            if (pkt->version != qc->version) {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"quic version mismatch: 0x%xD\", pkt->version);\n                return NGX_DECLINED;\n            }\n\n            if (pkt->first) {\n                if (ngx_quic_find_path(c, c->udp->dgram->sockaddr,\n                                       c->udp->dgram->socklen)\n                    == NULL)\n                {\n                    /* packet comes from unknown path, possibly migration */\n                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                                  \"quic too early migration attempt\");\n                    return NGX_DECLINED;\n                }\n            }\n\n            if (ngx_quic_check_csid(qc, pkt) != NGX_OK) {\n                return NGX_DECLINED;\n            }\n\n        }\n\n        rc = ngx_quic_process_payload(c, pkt);\n\n        if (rc == NGX_DECLINED && pkt->level == ssl_encryption_application) {\n            if (ngx_quic_process_stateless_reset(c, pkt) == NGX_OK) {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"quic stateless reset packet detected\");\n\n                qc->draining = 1;\n                ngx_quic_close_connection(c, NGX_OK);\n\n                return NGX_OK;\n            }\n        }\n\n        return rc;\n    }\n\n    /* packet does not belong to a connection */\n\n    if (rc == NGX_ABORT) {\n        return ngx_quic_negotiate_version(c, pkt);\n    }\n\n    if (pkt->level == ssl_encryption_application) {\n        return ngx_quic_send_stateless_reset(c, conf, pkt);\n    }\n\n    if (pkt->level != ssl_encryption_initial) {\n        return NGX_ERROR;\n    }\n\n    c->log->action = \"processing initial packet\";\n\n    if (pkt->dcid.len < NGX_QUIC_CID_LEN_MIN) {\n        /* RFC 9000, 7.2.  Negotiating Connection IDs */\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic too short dcid in initial\"\n                      \" packet: len:%i\", pkt->dcid.len);\n        return NGX_ERROR;\n    }\n\n    /* process retry and initialize connection IDs */\n\n    if (pkt->token.len) {\n\n        rc = ngx_quic_validate_token(c, conf->av_token_key, pkt);\n\n        if (rc == NGX_ERROR) {\n            /* internal error */\n            return NGX_ERROR;\n\n        } else if (rc == NGX_ABORT) {\n            /* token cannot be decrypted */\n            return ngx_quic_send_early_cc(c, pkt,\n                                          NGX_QUIC_ERR_INVALID_TOKEN,\n                                          \"cannot decrypt token\");\n        } else if (rc == NGX_DECLINED) {\n            /* token is invalid */\n\n            if (pkt->retried) {\n                /* invalid address validation token */\n                return ngx_quic_send_early_cc(c, pkt,\n                                          NGX_QUIC_ERR_INVALID_TOKEN,\n                                          \"invalid address validation token\");\n            } else if (conf->retry) {\n                /* invalid NEW_TOKEN */\n                return ngx_quic_send_retry(c, conf, pkt);\n            }\n        }\n\n        /* NGX_OK */\n\n    } else if (conf->retry) {\n        return ngx_quic_send_retry(c, conf, pkt);\n\n    } else {\n        pkt->odcid = pkt->dcid;\n    }\n\n    if (ngx_terminate || ngx_exiting) {\n        if (conf->retry) {\n            return ngx_quic_send_retry(c, conf, pkt);\n        }\n\n        return NGX_ERROR;\n    }\n\n    c->log->action = \"creating quic connection\";\n\n    qc = ngx_quic_new_connection(c, conf, pkt);\n    if (qc == NULL) {\n        return NGX_ERROR;\n    }\n\n    return ngx_quic_process_payload(c, pkt);\n}\n\n\nstatic ngx_int_t\nngx_quic_process_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)\n{\n    ngx_int_t               rc;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n    static u_char           buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n\n    qc = ngx_quic_get_connection(c);\n\n    qc->error = 0;\n    qc->error_reason = 0;\n\n    c->log->action = \"decrypting packet\";\n\n    if (!ngx_quic_keys_available(qc->keys, pkt->level)) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic no %s keys, ignoring packet\",\n                      ngx_quic_level_name(pkt->level));\n        return NGX_DECLINED;\n    }\n\n#if !defined (OPENSSL_IS_BORINGSSL)\n    /* OpenSSL provides read keys for an application level before it's ready */\n\n    if (pkt->level == ssl_encryption_application\n        && SSL_quic_read_level(c->ssl->connection)\n           < ssl_encryption_application)\n    {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic no %s keys ready, ignoring packet\",\n                      ngx_quic_level_name(pkt->level));\n        return NGX_DECLINED;\n    }\n#endif\n\n    pkt->keys = qc->keys;\n    pkt->key_phase = qc->key_phase;\n    pkt->plaintext = buf;\n\n    ctx = ngx_quic_get_send_ctx(qc, pkt->level);\n\n    rc = ngx_quic_decrypt(pkt, &ctx->largest_pn);\n    if (rc != NGX_OK) {\n        qc->error = pkt->error;\n        qc->error_reason = \"failed to decrypt packet\";\n        return rc;\n    }\n\n    pkt->decrypted = 1;\n\n    if (pkt->first) {\n        if (ngx_quic_update_paths(c, pkt) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (c->ssl == NULL) {\n        if (ngx_quic_init_connection(c) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (pkt->level == ssl_encryption_handshake) {\n        /*\n         * RFC 9001, 4.9.1.  Discarding Initial Keys\n         *\n         * The successful use of Handshake packets indicates\n         * that no more Initial packets need to be exchanged\n         */\n        ngx_quic_discard_ctx(c, ssl_encryption_initial);\n\n        if (qc->socket->path->state != NGX_QUIC_PATH_VALIDATED) {\n            qc->socket->path->state = NGX_QUIC_PATH_VALIDATED;\n            ngx_post_event(&qc->push, &ngx_posted_events);\n        }\n    }\n\n    if (qc->closing) {\n        /*\n         * RFC 9000, 10.2.  Immediate Close\n         *\n         * ... delayed or reordered packets are properly discarded.\n         *\n         *  In the closing state, an endpoint retains only enough information\n         *  to generate a packet containing a CONNECTION_CLOSE frame and to\n         *  identify packets as belonging to the connection.\n         */\n\n        qc->error_level = pkt->level;\n        qc->error = NGX_QUIC_ERR_NO_ERROR;\n        qc->error_reason = \"connection is closing, packet discarded\";\n        qc->error_ftype = 0;\n        qc->error_app = 0;\n\n        return ngx_quic_send_cc(c);\n    }\n\n    pkt->received = ngx_current_msec;\n\n    c->log->action = \"handling payload\";\n\n    if (pkt->level != ssl_encryption_application) {\n        return ngx_quic_handle_frames(c, pkt);\n    }\n\n    if (!pkt->key_update) {\n        return ngx_quic_handle_frames(c, pkt);\n    }\n\n    /* switch keys and generate next on Key Phase change */\n\n    qc->key_phase ^= 1;\n    ngx_quic_keys_switch(c, qc->keys);\n\n    rc = ngx_quic_handle_frames(c, pkt);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    return ngx_quic_keys_update(c, qc->keys);\n}\n\n\nvoid\nngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)\n{\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *f;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n    ngx_quic_fqueue_t      *fqueue;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (!ngx_quic_keys_available(qc->keys, level)) {\n        return;\n    }\n\n    ngx_quic_keys_discard(qc->keys, level);\n\n    qc->pto_count = 0;\n\n    ctx = ngx_quic_get_send_ctx(qc, level);\n\n    ngx_quic_free_bufs(c, ctx->crypto);\n\n    while (!ngx_queue_empty(&ctx->sent)) {\n        q = ngx_queue_head(&ctx->sent);\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        ngx_quic_queue_frame_remove(qc, &ctx->sent, f);\n\n        ngx_quic_congestion_ack(c, f);\n        ngx_quic_free_frame(c, f);\n    }\n\n    while (!ngx_queue_empty(&ctx->fqueues)) {\n        q = ngx_queue_head(&ctx->fqueues);\n        ngx_queue_remove(q);\n\n        fqueue = ngx_queue_data(q, ngx_quic_fqueue_t, queue);\n\n        while (!ngx_queue_empty(fqueue->frames)) {\n            q = ngx_queue_head(fqueue->frames);\n            f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n            ngx_quic_queue_frame_remove(qc, fqueue->frames, f);\n\n            ngx_quic_congestion_ack(c, f);\n            ngx_quic_free_frame(c, f);\n        }\n    }\n\n    if (level == ssl_encryption_initial) {\n        /* close temporary listener with odcid */\n        qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN);\n        if (qsock) {\n            ngx_quic_close_socket(c, qsock);\n        }\n    }\n\n    ctx->send_ack = 0;\n\n    ngx_quic_set_lost_timer(c);\n}\n\n\nstatic ngx_int_t\nngx_quic_check_csid(ngx_quic_connection_t *qc, ngx_quic_header_t *pkt)\n{\n    ngx_queue_t           *q;\n    ngx_quic_client_id_t  *cid;\n\n    for (q = ngx_queue_head(&qc->client_ids);\n         q != ngx_queue_sentinel(&qc->client_ids);\n         q = ngx_queue_next(q))\n    {\n        cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);\n\n        if (pkt->scid.len == cid->len\n            && ngx_memcmp(pkt->scid.data, cid->id, cid->len) == 0)\n        {\n            return NGX_OK;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_INFO, pkt->log, 0, \"quic unexpected quic scid\");\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt)\n{\n    u_char                 *end, *p;\n    ssize_t                 len;\n    ngx_buf_t               buf;\n    ngx_uint_t              do_close, nonprobing;\n    ngx_chain_t             chain;\n    ngx_quic_frame_t        frame;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    p = pkt->payload.data;\n    end = p + pkt->payload.len;\n\n    do_close = 0;\n    nonprobing = 0;\n\n    while (p < end) {\n\n        c->log->action = \"parsing frames\";\n\n        ngx_memzero(&frame, sizeof(ngx_quic_frame_t));\n        ngx_memzero(&buf, sizeof(ngx_buf_t));\n        buf.temporary = 1;\n\n        chain.buf = &buf;\n        chain.next = NULL;\n        frame.data = &chain;\n\n        len = ngx_quic_parse_frame(pkt, p, end, &frame);\n\n        if (len < 0) {\n            qc->error = pkt->error;\n            return NGX_ERROR;\n        }\n\n        ngx_quic_log_frame(c->log, &frame, 0);\n\n        c->log->action = \"handling frames\";\n\n        p += len;\n\n        switch (frame.type) {\n        /* probing frames */\n        case NGX_QUIC_FT_PADDING:\n        case NGX_QUIC_FT_PATH_CHALLENGE:\n        case NGX_QUIC_FT_PATH_RESPONSE:\n        case NGX_QUIC_FT_NEW_CONNECTION_ID:\n            break;\n\n        /* non-probing frames */\n        default:\n            nonprobing = 1;\n            break;\n        }\n\n        switch (frame.type) {\n\n        case NGX_QUIC_FT_ACK:\n            if (ngx_quic_handle_ack_frame(c, pkt, &frame) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            continue;\n\n        case NGX_QUIC_FT_PADDING:\n            /* no action required */\n            continue;\n\n        case NGX_QUIC_FT_CONNECTION_CLOSE:\n        case NGX_QUIC_FT_CONNECTION_CLOSE_APP:\n            do_close = 1;\n            continue;\n        }\n\n        /* got there with ack-eliciting packet */\n        pkt->need_ack = 1;\n\n        switch (frame.type) {\n\n        case NGX_QUIC_FT_CRYPTO:\n\n            if (ngx_quic_handle_crypto_frame(c, pkt, &frame) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_PING:\n            break;\n\n        case NGX_QUIC_FT_STREAM:\n\n            if (ngx_quic_handle_stream_frame(c, pkt, &frame) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_MAX_DATA:\n\n            if (ngx_quic_handle_max_data_frame(c, &frame.u.max_data) != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_STREAMS_BLOCKED:\n        case NGX_QUIC_FT_STREAMS_BLOCKED2:\n\n            if (ngx_quic_handle_streams_blocked_frame(c, pkt,\n                                                      &frame.u.streams_blocked)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_DATA_BLOCKED:\n\n            if (ngx_quic_handle_data_blocked_frame(c, pkt,\n                                                   &frame.u.data_blocked)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_STREAM_DATA_BLOCKED:\n\n            if (ngx_quic_handle_stream_data_blocked_frame(c, pkt,\n                                                  &frame.u.stream_data_blocked)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_MAX_STREAM_DATA:\n\n            if (ngx_quic_handle_max_stream_data_frame(c, pkt,\n                                                      &frame.u.max_stream_data)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_RESET_STREAM:\n\n            if (ngx_quic_handle_reset_stream_frame(c, pkt,\n                                                   &frame.u.reset_stream)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_STOP_SENDING:\n\n            if (ngx_quic_handle_stop_sending_frame(c, pkt,\n                                                   &frame.u.stop_sending)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_MAX_STREAMS:\n        case NGX_QUIC_FT_MAX_STREAMS2:\n\n            if (ngx_quic_handle_max_streams_frame(c, pkt, &frame.u.max_streams)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_PATH_CHALLENGE:\n\n            if (ngx_quic_handle_path_challenge_frame(c, &frame.u.path_challenge)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_PATH_RESPONSE:\n\n            if (ngx_quic_handle_path_response_frame(c, &frame.u.path_response)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_NEW_CONNECTION_ID:\n\n            if (ngx_quic_handle_new_connection_id_frame(c, &frame.u.ncid)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        case NGX_QUIC_FT_RETIRE_CONNECTION_ID:\n\n            if (ngx_quic_handle_retire_connection_id_frame(c,\n                                                           &frame.u.retire_cid)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        default:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic missing frame handler\");\n            return NGX_ERROR;\n        }\n    }\n\n    if (p != end) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic trailing garbage in payload:%ui bytes\", end - p);\n\n        qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;\n        return NGX_ERROR;\n    }\n\n    if (do_close) {\n        qc->draining = 1;\n        ngx_quic_close_connection(c, NGX_OK);\n    }\n\n    qsock = ngx_quic_get_socket(c);\n\n    if (qsock != qc->socket) {\n\n        if (qsock->path != qc->socket->path && nonprobing) {\n            /*\n             * RFC 9000, 9.2.  Initiating Connection Migration\n             *\n             * An endpoint can migrate a connection to a new local\n             * address by sending packets containing non-probing frames\n             * from that address.\n             */\n            if (ngx_quic_handle_migration(c, pkt) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n        /*\n         * else: packet arrived via non-default socket;\n         *       no reason to change active path\n         */\n    }\n\n    if (ngx_quic_ack_packet(c, pkt) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_quic_push_handler(ngx_event_t *ev)\n{\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"quic push timer\");\n\n    c = ev->data;\n\n    if (ngx_quic_output(c) != NGX_OK) {\n        ngx_quic_close_connection(c, NGX_ERROR);\n        return;\n    }\n\n    ngx_quic_connstate_dbg(c);\n}\n\n\nvoid\nngx_quic_shutdown_quic(ngx_connection_t *c, ngx_int_t rc)\n{\n    ngx_rbtree_t           *tree;\n    ngx_rbtree_node_t      *node;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc->closing) {\n        return;\n    }\n\n    tree = &qc->streams.tree;\n\n    if (tree->root != tree->sentinel) {\n        for (node = ngx_rbtree_min(tree->root, tree->sentinel);\n             node;\n             node = ngx_rbtree_next(tree, node))\n        {\n            qs = (ngx_quic_stream_t *) node;\n\n            if (!qs->cancelable) {\n                return;\n            }\n        }\n    }\n\n    ngx_quic_finalize_connection(c, rc, qc->shutdown_code, qc->shutdown_reason);\n}\n\n\nuint32_t\nngx_quic_version(ngx_connection_t *c)\n{\n    uint32_t                version;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    version = qc->version;\n\n    return (version & 0xff000000) == 0xff000000 ? version & 0xff : version;\n}\n\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\nsize_t\nngx_quic_mtu(ngx_connection_t *c)\n{\n    ngx_quic_connection_t  *qc;\n    ngx_quic_stream_t      *qs;\n    ngx_connection_t       *pc;\n\n    if (c->quic == NULL) {\n        return (size_t) NGX_ERROR;\n    }\n\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n\n    return qc->ctp.max_udp_payload_size;\n}\n#endif\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_H_INCLUDED_\n#define _NGX_EVENT_QUIC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_QUIC_MAX_UDP_PAYLOAD_SIZE        65527\n\n#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT  3\n#define NGX_QUIC_DEFAULT_MAX_ACK_DELAY       25\n#define NGX_QUIC_DEFAULT_HOST_KEY_LEN        32\n#define NGX_QUIC_SR_KEY_LEN                  32\n#define NGX_QUIC_AV_KEY_LEN                  32\n\n#define NGX_QUIC_SR_TOKEN_LEN                16\n\n#define NGX_QUIC_MIN_INITIAL_SIZE            1100\n\n#define NGX_QUIC_STREAM_SERVER_INITIATED     0x01\n#define NGX_QUIC_STREAM_UNIDIRECTIONAL       0x02\n\n#define NGX_QUIC_STREAM_BUFSIZE              65536\n\n\ntypedef struct {\n    /* configurable */\n    ngx_msec_t                 max_idle_timeout;\n    ngx_msec_t                 max_ack_delay;\n\n    size_t                     max_udp_payload_size;\n    size_t                     initial_max_data;\n    size_t                     initial_max_stream_data_bidi_local;\n    size_t                     initial_max_stream_data_bidi_remote;\n    size_t                     initial_max_stream_data_uni;\n    ngx_uint_t                 initial_max_streams_bidi;\n    ngx_uint_t                 initial_max_streams_uni;\n    ngx_uint_t                 ack_delay_exponent;\n    ngx_uint_t                 active_connection_id_limit;\n    ngx_flag_t                 disable_active_migration;\n    ngx_str_t                  original_dcid;\n    ngx_str_t                  initial_scid;\n    ngx_str_t                  retry_scid;\n    u_char                     sr_token[NGX_QUIC_SR_TOKEN_LEN];\n\n    /* TODO */\n    void                      *preferred_address;\n} ngx_quic_tp_t;\n\n\ntypedef struct {\n    ngx_ssl_t                 *ssl;\n    ngx_quic_tp_t              tp;\n    ngx_flag_t                 retry;\n    ngx_flag_t                 gso_enabled;\n    ngx_flag_t                 migration_close_connection;\n    ngx_str_t                  host_key;\n    ngx_int_t                  stream_close_code;\n    ngx_int_t                  stream_reject_code_uni;\n    ngx_int_t                  stream_reject_code_bidi;\n    size_t                     stream_buf_size;\n    size_t                     initial_window;\n    size_t                     min_window;\n    u_char                     av_token_key[NGX_QUIC_AV_KEY_LEN];\n    u_char                     sr_token_key[NGX_QUIC_SR_KEY_LEN];\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    ngx_flag_t                 mtu;\n    ngx_int_t                  mtu_attemts;\n    size_t                     mtu_target;\n#endif\n\n    uint64_t                   stream_shuffle;\n    ngx_flag_t                 nodelay;\n\n#if (NGX_HAVE_UDP_SENDMMSG)\n    ngx_flag_t                 sendmmsg_enabled;\n#endif\n} ngx_quic_conf_t;\n\n\ntypedef struct {\n    ngx_queue_t                queue;\n    ngx_queue_t               *frames;\n    uint64_t                   count;\n\n    unsigned                   attached : 1;\n} ngx_quic_fqueue_t;\n\n\nstruct ngx_quic_stream_s {\n    ngx_rbtree_node_t          node;\n    ngx_queue_t                queue;\n    ngx_connection_t          *parent;\n    ngx_connection_t          *connection;\n    ngx_quic_fqueue_t         *fqueue;\n    uint64_t                   id;\n    uint64_t                   acked;\n    uint64_t                   send_max_data;\n    uint64_t                   recv_max_data;\n    uint64_t                   recv_offset;\n    uint64_t                   recv_window;\n    uint64_t                   recv_last;\n    uint64_t                   final_size;\n    ngx_chain_t               *in;\n    ngx_uint_t                 cancelable;  /* unsigned  cancelable:1; */\n};\n\n\nvoid ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf);\nngx_connection_t *ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi);\nvoid ngx_quic_finalize_connection(ngx_connection_t *c, ngx_int_t rc, ngx_uint_t err,\n    const char *reason);\nvoid ngx_quic_shutdown_connection(ngx_connection_t *c, ngx_int_t rc, ngx_uint_t err,\n    const char *reason);\nngx_int_t ngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err);\nngx_int_t ngx_quic_shutdown_stream(ngx_connection_t *c, int how);\nuint32_t ngx_quic_version(ngx_connection_t *c);\nngx_int_t ngx_quic_handle_read_event(ngx_event_t *rev, ngx_uint_t flags);\nngx_int_t ngx_quic_handle_write_event(ngx_event_t *wev, size_t lowat);\nngx_int_t ngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t len,\n    ngx_str_t *dcid);\nngx_int_t ngx_quic_derive_key(ngx_log_t *log, const char *label,\n    ngx_str_t *secret, ngx_str_t *salt, u_char *out, size_t len);\n\nvoid ngx_quic_add_exemptions(ngx_connection_t *c, size_t size);\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\nsize_t ngx_quic_mtu(ngx_connection_t *c);\n#endif\n\n#endif /* _NGX_EVENT_QUIC_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_ack.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#include <ngx_event_quic_connection.h>\n\n\n#define NGX_QUIC_MAX_ACK_GAP                 2\n\n/* RFC 9002, 6.1.1. Packet Threshold: kPacketThreshold */\n#define NGX_QUIC_PKT_THR                     3 /* packets */\n/* RFC 9002, 6.1.2. Time Threshold: kGranularity */\n#define NGX_QUIC_TIME_GRANULARITY            1 /* ms */\n\n/* RFC 9002, 7.6.1. Duration: kPersistentCongestionThreshold */\n#define NGX_QUIC_PERSISTENT_CONGESTION_THR   3\n\n\n/* send time of ACK'ed packets */\ntypedef struct {\n    ngx_msec_t                               max_pn;\n    ngx_msec_t                               oldest;\n    ngx_msec_t                               newest;\n} ngx_quic_ack_stat_t;\n\n\nstatic ngx_inline ngx_msec_t ngx_quic_lost_threshold(ngx_quic_connection_t *qc);\nstatic void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,\n    enum ssl_encryption_level_t level, ngx_msec_t send_time);\nstatic ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,\n    ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,\n    ngx_quic_ack_stat_t *st);\nstatic void ngx_quic_drop_ack_ranges(ngx_connection_t *c,\n    ngx_quic_send_ctx_t *ctx, uint64_t pn);\nstatic ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c,\n    ngx_quic_ack_stat_t *st);\nstatic ngx_msec_t ngx_quic_pcg_duration(ngx_connection_t *c);\nstatic void ngx_quic_persistent_congestion(ngx_connection_t *c);\nstatic void ngx_quic_congestion_lost(ngx_connection_t *c,\n    ngx_quic_frame_t *frame);\nstatic void ngx_quic_lost_handler(ngx_event_t *ev);\nstatic ngx_int_t ngx_quic_is_blocked(ngx_connection_t *c);\n\n\n/* RFC 9002, 6.1.2. Time Threshold: kTimeThreshold, kGranularity */\nstatic ngx_inline ngx_msec_t\nngx_quic_lost_threshold(ngx_quic_connection_t *qc)\n{\n    ngx_msec_t  thr;\n\n    thr = ngx_max(qc->latest_rtt, qc->avg_rtt);\n    thr += thr >> 3;\n\n    return ngx_max(thr, NGX_QUIC_TIME_GRANULARITY);\n}\n\n\nngx_int_t\nngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,\n    ngx_quic_frame_t *f)\n{\n    ssize_t                 n;\n    u_char                 *pos, *end;\n    uint64_t                min, max, gap, range;\n    ngx_uint_t              i;\n    ngx_quic_ack_stat_t     send_time;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_ack_frame_t   *ack;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    ctx = ngx_quic_get_send_ctx(qc, pkt->level);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_handle_ack_frame level:%d\", pkt->level);\n\n    ack = &f->u.ack;\n\n    /*\n     * RFC 9000, 19.3.1.  ACK Ranges\n     *\n     *  If any computed packet number is negative, an endpoint MUST\n     *  generate a connection error of type FRAME_ENCODING_ERROR.\n     */\n\n    if (ack->first_range > ack->largest) {\n        qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic invalid first range in ack frame\");\n        return NGX_ERROR;\n    }\n\n    min = ack->largest - ack->first_range;\n    max = ack->largest;\n\n    send_time.oldest = NGX_TIMER_INFINITE;\n    send_time.newest = NGX_TIMER_INFINITE;\n\n    if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    /* RFC 9000, 13.2.4.  Limiting Ranges by Tracking ACK Frames */\n    if (ctx->largest_ack < max || ctx->largest_ack == NGX_QUIC_UNSET_PN) {\n        ctx->largest_ack = max;\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic updated largest received ack:%uL\", max);\n\n        /*\n         * RFC 9002, 5.1.  Generating RTT Samples\n         *\n         *  An endpoint generates an RTT sample on receiving an\n         *  ACK frame that meets the following two conditions:\n         *\n         *  - the largest acknowledged packet number is newly acknowledged\n         *  - at least one of the newly acknowledged packets was ack-eliciting.\n         */\n\n        if (send_time.max_pn != NGX_TIMER_INFINITE) {\n            ngx_quic_rtt_sample(c, ack, pkt->level, send_time.max_pn);\n        }\n    }\n\n    if (f->data) {\n        pos = f->data->buf->pos;\n        end = f->data->buf->last;\n\n    } else {\n        pos = NULL;\n        end = NULL;\n    }\n\n    for (i = 0; i < ack->range_count; i++) {\n\n        n = ngx_quic_parse_ack_range(pkt->log, pos, end, &gap, &range);\n        if (n == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n        pos += n;\n\n        if (gap + 2 > min) {\n            qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"quic invalid range:%ui in ack frame\", i);\n            return NGX_ERROR;\n        }\n\n        max = min - gap - 2;\n\n        if (range > max) {\n            qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"quic invalid range:%ui in ack frame\", i);\n            return NGX_ERROR;\n        }\n\n        min = max - range;\n\n        if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return ngx_quic_detect_lost(c, &send_time);\n}\n\n\nstatic void\nngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,\n    enum ssl_encryption_level_t level, ngx_msec_t send_time)\n{\n    ngx_msec_t              latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    latest_rtt = ngx_current_msec - send_time;\n    qc->latest_rtt = latest_rtt;\n\n    if (qc->min_rtt == NGX_TIMER_INFINITE) {\n        qc->min_rtt = latest_rtt;\n        qc->avg_rtt = latest_rtt;\n        qc->rttvar = latest_rtt / 2;\n        qc->first_rtt = ngx_current_msec;\n\n    } else {\n        qc->min_rtt = ngx_min(qc->min_rtt, latest_rtt);\n\n        ack_delay = ack->delay * (1 << qc->ctp.ack_delay_exponent) / 1000;\n\n        if (c->ssl->handshaked) {\n            ack_delay = ngx_min(ack_delay, qc->ctp.max_ack_delay);\n        }\n\n        adjusted_rtt = latest_rtt;\n\n        if (qc->min_rtt + ack_delay < latest_rtt) {\n            adjusted_rtt -= ack_delay;\n        }\n\n        qc->avg_rtt += (adjusted_rtt >> 3) - (qc->avg_rtt >> 3);\n        rttvar_sample = ngx_abs((ngx_msec_int_t) (qc->avg_rtt - adjusted_rtt));\n        qc->rttvar += (rttvar_sample >> 2) - (qc->rttvar >> 2);\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic rtt sample latest:%M min:%M avg:%M var:%M\",\n                   latest_rtt, qc->min_rtt, qc->avg_rtt, qc->rttvar);\n}\n\n\nstatic ngx_int_t\nngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    uint64_t min, uint64_t max, ngx_quic_ack_stat_t *st)\n{\n    ngx_uint_t              found;\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *f;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    st->max_pn = NGX_TIMER_INFINITE;\n    found = 0;\n\n    q = ngx_queue_head(&ctx->sent);\n\n    while (q != ngx_queue_sentinel(&ctx->sent)) {\n\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n        q = ngx_queue_next(q);\n\n        if (f->pnum > max) {\n            break;\n        }\n\n        if (f->pnum >= min) {\n            ngx_quic_congestion_ack(c, f);\n\n            switch (f->type) {\n            case NGX_QUIC_FT_ACK:\n            case NGX_QUIC_FT_ACK_ECN:\n                ngx_quic_drop_ack_ranges(c, ctx, f->u.ack.largest);\n                break;\n\n            case NGX_QUIC_FT_STREAM:\n                ngx_quic_handle_stream_ack(c, f);\n                break;\n            }\n\n            if (f->pnum == max) {\n                st->max_pn = f->last;\n            }\n\n            /* save earliest and latest send times of frames ack'ed */\n            if (st->oldest == NGX_TIMER_INFINITE || f->last < st->oldest) {\n                st->oldest = f->last;\n            }\n\n            if (st->newest == NGX_TIMER_INFINITE || f->last > st->newest) {\n                st->newest = f->last;\n            }\n\n            ngx_quic_queue_frame_remove(qc, &ctx->sent, f);\n            ngx_quic_free_frame(c, f);\n            found = 1;\n        }\n    }\n\n    if (!found) {\n\n        if (max < ctx->pnum) {\n            /* duplicate ACK or ACK for non-ack-eliciting frame */\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic ACK for the packet not sent\");\n\n        qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;\n        qc->error_ftype = NGX_QUIC_FT_ACK;\n        qc->error_reason = \"unknown packet number\";\n\n        return NGX_ERROR;\n    }\n\n    if (!qc->push.timer_set) {\n        ngx_post_event(&qc->push, &ngx_posted_events);\n    }\n\n    qc->pto_count = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_is_blocked(ngx_connection_t *c)\n{\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n    ngx_uint_t              i;\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n\n    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n        ctx = &qc->send_ctx[i];\n\n        if (ctx->last_priority) {\n            return 0;\n        }\n    }\n\n    if (cg->in_flight < cg->window) {\n        return 0;\n    }\n\n    return 1;\n}\n\n\nvoid\nngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)\n{\n    ngx_uint_t              blocked;\n    ngx_msec_t              timer;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n\n    if (f->plen == 0) {\n        return;\n    }\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    if (f->probe) {\n        ngx_quic_mtu_ack(c, f);\n        return;\n    }\n#endif\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n\n    blocked = ngx_quic_is_blocked(c);\n\n    cg->in_flight -= f->plen;\n\n    timer = f->last - cg->recovery_start;\n\n    if ((ngx_msec_int_t) timer <= 0) {\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic congestion ack recovery win:%uz ss:%z if:%uz\",\n                       cg->window, cg->ssthresh, cg->in_flight);\n\n        goto done;\n    }\n\n    if (cg->window < cg->ssthresh) {\n        cg->window += f->plen;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic congestion slow start win:%uz ss:%z if:%uz\",\n                       cg->window, cg->ssthresh, cg->in_flight);\n\n    } else {\n        cg->window += qc->tp.max_udp_payload_size * f->plen / cg->window;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic congestion avoidance win:%uz ss:%z if:%uz\",\n                       cg->window, cg->ssthresh, cg->in_flight);\n    }\n\n    /* prevent recovery_start from wrapping */\n\n    timer = cg->recovery_start - ngx_current_msec + qc->tp.max_idle_timeout * 2;\n\n    if ((ngx_msec_int_t) timer < 0) {\n        cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;\n    }\n\ndone:\n\n    if (blocked && !ngx_quic_is_blocked(c)) {\n        ngx_post_event(&qc->push, &ngx_posted_events);\n    }\n}\n\n\nstatic void\nngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    uint64_t pn)\n{\n    uint64_t               base;\n    ngx_uint_t             i, smallest, largest;\n    ngx_quic_ack_range_t  *r;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_drop_ack_ranges pn:%uL largest:%uL\"\n                   \" fr:%uL nranges:%ui\", pn, ctx->largest_range,\n                   ctx->first_range, ctx->nranges);\n\n    base = ctx->largest_range;\n\n    if (base == NGX_QUIC_UNSET_PN) {\n        return;\n    }\n\n    if (ctx->pending_ack != NGX_QUIC_UNSET_PN && pn >= ctx->pending_ack) {\n        ctx->pending_ack = NGX_QUIC_UNSET_PN;\n    }\n\n    largest = base;\n    smallest = largest - ctx->first_range;\n\n    if (pn >= largest) {\n        ctx->largest_range = NGX_QUIC_UNSET_PN;\n        ctx->first_range = 0;\n        ctx->nranges = 0;\n        return;\n    }\n\n    if (pn >= smallest) {\n        ctx->first_range = largest - pn - 1;\n        ctx->nranges = 0;\n        return;\n    }\n\n    for (i = 0; i < ctx->nranges; i++) {\n        r = &ctx->ranges[i];\n\n        largest = smallest - r->gap - 2;\n        smallest = largest - r->range;\n\n        if (pn >= largest) {\n            ctx->nranges = i;\n            return;\n        }\n        if (pn >= smallest) {\n            r->range = largest - pn - 1;\n            ctx->nranges = i + 1;\n            return;\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)\n{\n    ngx_uint_t              i, nlost;\n    ngx_msec_t              now, wait, thr, oldest, newest;\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *start;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    now = ngx_current_msec;\n    thr = ngx_quic_lost_threshold(qc);\n\n    /* send time of lost packets across all send contexts */\n    oldest = NGX_TIMER_INFINITE;\n    newest = NGX_TIMER_INFINITE;\n\n    nlost = 0;\n\n    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n\n        ctx = &qc->send_ctx[i];\n\n        if (ctx->largest_ack == NGX_QUIC_UNSET_PN) {\n            continue;\n        }\n\n        while (!ngx_queue_empty(&ctx->sent)) {\n\n            q = ngx_queue_head(&ctx->sent);\n            start = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n            if (start->pnum > ctx->largest_ack) {\n                break;\n            }\n\n            wait = start->last + thr - now;\n\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic detect_lost pnum:%uL thr:%M wait:%i level:%d\",\n                           start->pnum, thr, (ngx_int_t) wait, start->level);\n\n            if ((ngx_msec_int_t) wait > 0\n                && ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)\n            {\n                break;\n            }\n\n            if (start->last > qc->first_rtt) {\n\n                if (oldest == NGX_TIMER_INFINITE || start->last < oldest) {\n                    oldest = start->last;\n                }\n\n                if (newest == NGX_TIMER_INFINITE || start->last > newest) {\n                    newest = start->last;\n                }\n\n                nlost++;\n            }\n\n            ngx_quic_resend_frames(c, ctx);\n        }\n    }\n\n\n    /* RFC 9002, 7.6.2.  Establishing Persistent Congestion */\n\n    /*\n     * Once acknowledged, packets are no longer tracked. Thus no send time\n     * information is available for such packets. This limits persistent\n     * congestion algorithm to packets mentioned within ACK ranges of the\n     * latest ACK frame.\n     */\n\n    if (st && nlost >= 2 && (st->newest < oldest || st->oldest > newest)) {\n\n        if (newest - oldest > ngx_quic_pcg_duration(c)) {\n            ngx_quic_persistent_congestion(c);\n        }\n    }\n\n    ngx_quic_set_lost_timer(c);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_msec_t\nngx_quic_pcg_duration(ngx_connection_t *c)\n{\n    ngx_msec_t              duration;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    duration = qc->avg_rtt;\n    duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);\n    duration += qc->ctp.max_ack_delay;\n    duration *= NGX_QUIC_PERSISTENT_CONGESTION_THR;\n\n    return duration;\n}\n\n\nstatic void\nngx_quic_persistent_congestion(ngx_connection_t *c)\n{\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n\n    cg->recovery_start = ngx_current_msec;\n    cg->window = qc->tp.max_udp_payload_size * 2;\n\n    if (qc->conf->min_window && cg->window < qc->conf->min_window) {\n        cg->window = qc->conf->min_window;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic persistent congestion win:%uz\", cg->window);\n}\n\n\nvoid\nngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)\n{\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *f, *start;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    q = ngx_queue_head(&ctx->sent);\n    start = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic resend packet pnum:%uL\", start->pnum);\n\n    ngx_quic_congestion_lost(c, start);\n\n    do {\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        if (f->pnum != start->pnum) {\n            break;\n        }\n\n        q = ngx_queue_next(q);\n\n        ngx_quic_queue_frame_remove(qc, &ctx->sent, f);\n\n        switch (f->type) {\n        case NGX_QUIC_FT_ACK:\n        case NGX_QUIC_FT_ACK_ECN:\n            if (ctx->level == ssl_encryption_application) {\n                /* force generation of most recent acknowledgment */\n                ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;\n            }\n\n            ngx_quic_free_frame(c, f);\n            break;\n\n        case NGX_QUIC_FT_PING:\n        case NGX_QUIC_FT_PATH_RESPONSE:\n        case NGX_QUIC_FT_CONNECTION_CLOSE:\n            ngx_quic_free_frame(c, f);\n            break;\n\n        case NGX_QUIC_FT_MAX_DATA:\n            f->u.max_data.max_data = qc->streams.recv_max_data;\n            ngx_quic_queue_frame_priority(qc, f, 1);\n            break;\n\n        case NGX_QUIC_FT_MAX_STREAMS:\n        case NGX_QUIC_FT_MAX_STREAMS2:\n            f->u.max_streams.limit = f->u.max_streams.bidi\n                                     ? qc->streams.client_max_streams_bidi\n                                     : qc->streams.client_max_streams_uni;\n            ngx_quic_queue_frame_priority(qc, f, 1);\n            break;\n\n        case NGX_QUIC_FT_MAX_STREAM_DATA:\n            qs = ngx_quic_find_stream(&qc->streams.tree,\n                                      f->u.max_stream_data.id);\n            if (qs == NULL) {\n                ngx_quic_free_frame(c, f);\n                break;\n            }\n\n            f->u.max_stream_data.limit = qs->recv_max_data;\n            ngx_quic_queue_frame_priority(qc, f, 1);\n            break;\n\n        case NGX_QUIC_FT_STREAM:\n            qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id);\n\n            if (qs && qs->connection->write->error) {\n                /* RESET_STREAM was sent */\n                ngx_quic_free_frame(c, f);\n                break;\n            }\n\n            /* fall through */\n\n        default:\n            ngx_quic_queue_frame_priority(qc, f, 0);\n        }\n\n    } while (q != ngx_queue_sentinel(&ctx->sent));\n\n    if (qc->closing) {\n        return;\n    }\n\n    ngx_post_event(&qc->push, &ngx_posted_events);\n}\n\n\nstatic void\nngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)\n{\n    ngx_uint_t              blocked;\n    ngx_msec_t              timer;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n\n    if (f->plen == 0) {\n        return;\n    }\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    if (f->probe) {\n        ngx_quic_mtu_lost(c, f);\n        return;\n    }\n#endif\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n\n    blocked =  ngx_quic_is_blocked(c);\n\n    cg->in_flight -= f->plen;\n    f->plen = 0;\n\n    timer = f->last - cg->recovery_start;\n\n    if ((ngx_msec_int_t) timer <= 0) {\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic congestion lost recovery win:%uz ss:%z if:%uz\",\n                       cg->window, cg->ssthresh, cg->in_flight);\n\n        goto done;\n    }\n\n    cg->recovery_start = ngx_current_msec;\n    cg->window /= 2;\n\n    if (cg->window < qc->tp.max_udp_payload_size * 2) {\n        cg->window = qc->tp.max_udp_payload_size * 2;\n    }\n\n    if (qc->conf->min_window && cg->window < qc->conf->min_window) {\n        cg->window = qc->conf->min_window;\n    }\n\n    cg->ssthresh = cg->window;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic congestion lost win:%uz ss:%z if:%uz\",\n                   cg->window, cg->ssthresh, cg->in_flight);\n\ndone:\n\n    if (blocked && !ngx_quic_is_blocked(c)) {\n        ngx_post_event(&qc->push, &ngx_posted_events);\n    }\n}\n\n\nvoid\nngx_quic_set_lost_timer(ngx_connection_t *c)\n{\n    ngx_uint_t              i;\n    ngx_msec_t              now;\n    ngx_queue_t            *q;\n    ngx_msec_int_t          lost, pto, w;\n    ngx_quic_frame_t       *f;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    now = ngx_current_msec;\n\n    lost = -1;\n    pto = -1;\n\n    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n        ctx = &qc->send_ctx[i];\n\n        if (ngx_queue_empty(&ctx->sent)) {\n            continue;\n        }\n\n        if (ctx->largest_ack != NGX_QUIC_UNSET_PN) {\n            q = ngx_queue_head(&ctx->sent);\n            f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n            w = (ngx_msec_int_t) (f->last + ngx_quic_lost_threshold(qc) - now);\n\n            if (f->pnum <= ctx->largest_ack) {\n                if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {\n                    w = 0;\n                }\n\n                if (lost == -1 || w < lost) {\n                    lost = w;\n                }\n            }\n        }\n\n        q = ngx_queue_last(&ctx->sent);\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n        w = (ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now);\n\n        if (w < 0) {\n            w = 0;\n        }\n\n        if (pto == -1 || w < pto) {\n            pto = w;\n        }\n    }\n\n    if (qc->pto.timer_set) {\n        ngx_del_timer(&qc->pto);\n    }\n\n    if (lost != -1) {\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic lost timer lost:%M\", lost);\n\n        qc->pto.handler = ngx_quic_lost_handler;\n        ngx_add_timer(&qc->pto, lost);\n        return;\n    }\n\n    if (pto != -1) {\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic lost timer pto:%M\", pto);\n\n        qc->pto.handler = ngx_quic_pto_handler;\n        ngx_add_timer(&qc->pto, pto);\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic lost timer unset\");\n}\n\n\nngx_msec_t\nngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)\n{\n    ngx_msec_t              duration;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    /* RFC 9002, Appendix A.8.  Setting the Loss Detection Timer */\n    duration = qc->avg_rtt;\n\n    duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);\n    duration <<= qc->pto_count;\n\n    if (qc->congestion.in_flight == 0) { /* no in-flight packets */\n        return duration;\n    }\n\n    if (ctx->level == ssl_encryption_application && c->ssl->handshaked) {\n        duration += qc->ctp.max_ack_delay << qc->pto_count;\n    }\n\n    return duration;\n}\n\n\nstatic\nvoid ngx_quic_lost_handler(ngx_event_t *ev)\n{\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"quic lost timer\");\n\n    c = ev->data;\n\n    if (ngx_quic_detect_lost(c, NULL) != NGX_OK) {\n        ngx_quic_close_connection(c, NGX_ERROR);\n    }\n\n    ngx_quic_connstate_dbg(c);\n}\n\n\nvoid\nngx_quic_pto_handler(ngx_event_t *ev)\n{\n    ngx_uint_t              i;\n    ngx_msec_t              now;\n    ngx_queue_t            *q, *next, *sq;\n    ngx_connection_t       *c;\n    ngx_quic_frame_t       *f;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n    ngx_quic_fqueue_t      *fqueue;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"quic pto timer\");\n\n    c = ev->data;\n    qc = ngx_quic_get_connection(c);\n    now = ngx_current_msec;\n\n    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n\n         ctx = &qc->send_ctx[i];\n\n        if (ngx_queue_empty(&ctx->sent)) {\n            continue;\n        }\n\n        q = ngx_queue_head(&ctx->sent);\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        if (f->pnum <= ctx->largest_ack\n            && ctx->largest_ack != NGX_QUIC_UNSET_PN)\n        {\n            continue;\n        }\n\n        if ((ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now) > 0) {\n            continue;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic pto %s pto_count:%ui\",\n                       ngx_quic_level_name(ctx->level), qc->pto_count);\n\n        for (sq = ngx_queue_head(&ctx->fqueues);\n            sq != ngx_queue_sentinel(&ctx->fqueues);\n            sq = ngx_queue_next(sq))\n        {\n            fqueue = ngx_queue_data(sq, ngx_quic_fqueue_t, queue);\n\n            for (q = ngx_queue_head(fqueue->frames);\n                 q != ngx_queue_sentinel(fqueue->frames);\n                 /* void */)\n            {\n                next = ngx_queue_next(q);\n                f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n                if (f->type == NGX_QUIC_FT_PING) {\n                    ngx_quic_queue_frame_remove(qc, fqueue->frames, f);\n                    ngx_quic_free_frame(c, f);\n                }\n\n                q = next;\n            }\n        }\n\n        for (q = ngx_queue_head(&ctx->sent);\n             q != ngx_queue_sentinel(&ctx->sent);\n             /* void */)\n        {\n            next = ngx_queue_next(q);\n            f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n            if (f->type == NGX_QUIC_FT_PING) {\n                ngx_quic_congestion_lost(c, f);\n                ngx_quic_queue_frame_remove(qc, &ctx->sent, f);\n                ngx_quic_free_frame(c, f);\n            }\n\n            q = next;\n        }\n\n        /* enforce 2 udp datagrams */\n\n        f = ngx_quic_alloc_frame(c);\n        if (f == NULL) {\n            break;\n        }\n\n        f->level = ctx->level;\n        f->type = NGX_QUIC_FT_PING;\n        f->flush = 1;\n        f->need_ack = 1;\n\n        ngx_quic_queue_frame_priority(qc, f, 1);\n\n        f = ngx_quic_alloc_frame(c);\n        if (f == NULL) {\n            break;\n        }\n\n        f->level = ctx->level;\n        f->type = NGX_QUIC_FT_PING;\n        f->flush = 1;\n        f->need_ack = 1;\n\n        ngx_quic_queue_frame_priority(qc, f, 1);\n    }\n\n    qc->pto_count++;\n\n    ngx_quic_connstate_dbg(c);\n}\n\n\nngx_int_t\nngx_quic_ack_packet(ngx_connection_t *c, ngx_quic_header_t *pkt)\n{\n    uint64_t                base, largest, smallest, gs, ge, gap, range, pn;\n    uint64_t                prev_pending;\n    ngx_uint_t              i, nr;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_ack_range_t   *r;\n    ngx_quic_connection_t  *qc;\n\n    c->log->action = \"preparing ack\";\n\n    qc = ngx_quic_get_connection(c);\n\n    ctx = ngx_quic_get_send_ctx(qc, pkt->level);\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_ack_packet pn:%uL largest %L fr:%uL\"\n                   \" nranges:%ui\", pkt->pn, (int64_t) ctx->largest_range,\n                   ctx->first_range, ctx->nranges);\n\n    prev_pending = ctx->pending_ack;\n\n    if (pkt->need_ack) {\n\n        ngx_post_event(&qc->push, &ngx_posted_events);\n\n        if (ctx->send_ack == 0) {\n            ctx->ack_delay_start = ngx_current_msec;\n        }\n\n        ctx->send_ack++;\n\n        if (ctx->pending_ack == NGX_QUIC_UNSET_PN\n            || ctx->pending_ack < pkt->pn)\n        {\n            ctx->pending_ack = pkt->pn;\n        }\n    }\n\n    base = ctx->largest_range;\n    pn = pkt->pn;\n\n    if (base == NGX_QUIC_UNSET_PN) {\n        ctx->largest_range = pn;\n        ctx->largest_received = pkt->received;\n        return NGX_OK;\n    }\n\n    if (base == pn) {\n        return NGX_OK;\n    }\n\n    largest = base;\n    smallest = largest - ctx->first_range;\n\n    if (pn > base) {\n\n        if (pn - base == 1) {\n            ctx->first_range++;\n            ctx->largest_range = pn;\n            ctx->largest_received = pkt->received;\n\n            return NGX_OK;\n\n        } else {\n            /* new gap in front of current largest */\n\n            /* no place for new range, send current range as is */\n            if (ctx->nranges == NGX_QUIC_MAX_RANGES) {\n\n                if (prev_pending != NGX_QUIC_UNSET_PN) {\n                    if (ngx_quic_send_ack(c, ctx) != NGX_OK) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                if (prev_pending == ctx->pending_ack || !pkt->need_ack) {\n                    ctx->pending_ack = NGX_QUIC_UNSET_PN;\n                }\n            }\n\n            gap = pn - base - 2;\n            range = ctx->first_range;\n\n            ctx->first_range = 0;\n            ctx->largest_range = pn;\n            ctx->largest_received = pkt->received;\n\n            /* packet is out of order, force send */\n            if (pkt->need_ack) {\n                ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;\n            }\n\n            i = 0;\n\n            goto insert;\n        }\n    }\n\n    /*  pn < base, perform lookup in existing ranges */\n\n    /* packet is out of order */\n    if (pkt->need_ack) {\n        ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;\n    }\n\n    if (pn >= smallest && pn <= largest) {\n        return NGX_OK;\n    }\n\n#if (NGX_SUPPRESS_WARN)\n    r = NULL;\n#endif\n\n    for (i = 0; i < ctx->nranges; i++) {\n        r = &ctx->ranges[i];\n\n        ge = smallest - 1;\n        gs = ge - r->gap;\n\n        if (pn >= gs && pn <= ge) {\n\n            if (gs == ge) {\n                /* gap size is exactly one packet, now filled */\n\n                /* data moves to previous range, current is removed */\n\n                if (i == 0) {\n                    ctx->first_range += r->range + 2;\n\n                } else {\n                    ctx->ranges[i - 1].range += r->range + 2;\n                }\n\n                nr = ctx->nranges - i - 1;\n                if (nr) {\n                    ngx_memmove(&ctx->ranges[i], &ctx->ranges[i + 1],\n                                sizeof(ngx_quic_ack_range_t) * nr);\n                }\n\n                ctx->nranges--;\n\n            } else if (pn == gs) {\n                /* current gap shrinks from tail (current range grows) */\n                r->gap--;\n                r->range++;\n\n            } else if (pn == ge) {\n                /* current gap shrinks from head (previous range grows) */\n                r->gap--;\n\n                if (i == 0) {\n                    ctx->first_range++;\n\n                } else {\n                    ctx->ranges[i - 1].range++;\n                }\n\n            } else {\n                /* current gap is split into two parts */\n\n                gap = ge - pn - 1;\n                range = 0;\n\n                if (ctx->nranges == NGX_QUIC_MAX_RANGES) {\n                    if (prev_pending != NGX_QUIC_UNSET_PN) {\n                        if (ngx_quic_send_ack(c, ctx) != NGX_OK) {\n                            return NGX_ERROR;\n                        }\n                    }\n\n                    if (prev_pending == ctx->pending_ack || !pkt->need_ack) {\n                        ctx->pending_ack = NGX_QUIC_UNSET_PN;\n                    }\n                }\n\n                r->gap = pn - gs - 1;\n                goto insert;\n            }\n\n            return NGX_OK;\n        }\n\n        largest = smallest - r->gap - 2;\n        smallest = largest - r->range;\n\n        if (pn >= smallest && pn <= largest) {\n            /* this packet number is already known */\n            return NGX_OK;\n        }\n\n    }\n\n    if (pn == smallest - 1) {\n        /* extend first or last range */\n\n        if (i == 0) {\n            ctx->first_range++;\n\n        } else {\n            r->range++;\n        }\n\n        return NGX_OK;\n    }\n\n    /* nothing found, add new range at the tail  */\n\n    if (ctx->nranges == NGX_QUIC_MAX_RANGES) {\n        /* packet is too old to keep it */\n\n        if (pkt->need_ack) {\n            return ngx_quic_send_ack_range(c, ctx, pn, pn);\n        }\n\n        return NGX_OK;\n    }\n\n    gap = smallest - 2 - pn;\n    range = 0;\n\ninsert:\n\n    if (ctx->nranges < NGX_QUIC_MAX_RANGES) {\n        ctx->nranges++;\n    }\n\n    ngx_memmove(&ctx->ranges[i + 1], &ctx->ranges[i],\n                sizeof(ngx_quic_ack_range_t) * (ctx->nranges - i - 1));\n\n    ctx->ranges[i].gap = gap;\n    ctx->ranges[i].range = range;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)\n{\n    ngx_msec_t              delay;\n    ngx_quic_connection_t  *qc;\n\n    if (!ctx->send_ack) {\n        return NGX_OK;\n    }\n\n    if (ctx->level == ssl_encryption_application)  {\n\n        delay = ngx_current_msec - ctx->ack_delay_start;\n        qc = ngx_quic_get_connection(c);\n\n        if (!ctx->ack_immediately && ctx->send_ack < NGX_QUIC_MAX_ACK_GAP\n            && delay < qc->tp.max_ack_delay)\n        {\n            if (!qc->push.timer_set && !qc->closing) {\n                ngx_add_timer(&qc->push,\n                              qc->tp.max_ack_delay - delay);\n            }\n\n            return NGX_OK;\n        }\n\n        ctx->ack_immediately = 0;\n    }\n\n    if (ctx->pending_ack == NGX_QUIC_UNSET_PN) {\n        return NGX_OK;\n    }\n\n    if (ngx_quic_send_ack(c, ctx) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ctx->send_ack = 0;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_ack.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_ACK_H_INCLUDED_\n#define _NGX_EVENT_QUIC_ACK_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_frame_t *f);\n\nvoid ngx_quic_congestion_ack(ngx_connection_t *c,\n    ngx_quic_frame_t *frame);\nvoid ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);\nvoid ngx_quic_set_lost_timer(ngx_connection_t *c);\nvoid ngx_quic_pto_handler(ngx_event_t *ev);\nngx_msec_t ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);\n\nngx_int_t ngx_quic_ack_packet(ngx_connection_t *c,\n    ngx_quic_header_t *pkt);\nngx_int_t ngx_quic_generate_ack(ngx_connection_t *c,\n    ngx_quic_send_ctx_t *ctx);\n\n#endif /* _NGX_EVENT_QUIC_ACK_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_bpf.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_QUIC_BPF_VARNAME  \"NGINX_BPF_MAPS\"\n#define NGX_QUIC_BPF_VARSEP    ';'\n#define NGX_QUIC_BPF_FDSEP     '-'\n#define NGX_QUIC_BPF_ADDRSEP   '#'\n\n\n#define NGX_QUIC_BPF_ACTIVE_KEYS_MAX 256\n\n\n#define ngx_quic_bpf_get_conf(cycle)                                          \\\n    (ngx_quic_bpf_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_quic_bpf_module)\n\n#define ngx_quic_bpf_get_old_conf(cycle)                                      \\\n    cycle->old_cycle->conf_ctx ? ngx_quic_bpf_get_conf(cycle->old_cycle)      \\\n                               : NULL\n\n#define ngx_core_get_conf(cycle)                                              \\\n    (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module)\n\n\ntypedef struct {\n    ngx_queue_t           queue;\n    int                   map_fd;\n    int                   map_active_fd;\n\n    struct sockaddr      *sockaddr;\n    socklen_t             socklen;\n    ngx_uint_t            unused;     /* unsigned  unused:1; */\n} ngx_quic_sock_group_t;\n\n\ntypedef struct {\n    ngx_flag_t            enabled;\n    ngx_uint_t            map_size;\n    ngx_queue_t           groups;     /* of ngx_quic_sock_group_t */\n} ngx_quic_bpf_conf_t;\n\n\nstatic void *ngx_quic_bpf_create_conf(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_quic_bpf_module_init(ngx_cycle_t *cycle);\n\nstatic void ngx_quic_bpf_cleanup(void *data);\nstatic ngx_inline void ngx_quic_bpf_close(ngx_log_t *log, int fd,\n    const char *name);\n\nstatic ngx_quic_sock_group_t *ngx_quic_bpf_find_group(ngx_quic_bpf_conf_t *bcf,\n    ngx_listening_t *ls);\nstatic ngx_quic_sock_group_t *ngx_quic_bpf_alloc_group(ngx_cycle_t *cycle,\n    struct sockaddr *sa, socklen_t socklen);\nstatic ngx_quic_sock_group_t *ngx_quic_bpf_create_group(ngx_cycle_t *cycle,\n    ngx_listening_t *ls);\nstatic ngx_quic_sock_group_t *ngx_quic_bpf_get_group(ngx_cycle_t *cycle,\n    ngx_listening_t *ls);\nstatic ngx_int_t ngx_quic_bpf_group_add_socket(ngx_cycle_t *cycle,\n    ngx_listening_t *ls);\nstatic uint64_t ngx_quic_bpf_socket_key(ngx_fd_t fd, ngx_log_t *log);\n\nstatic ngx_int_t ngx_quic_bpf_export_maps(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_quic_bpf_import_maps(ngx_cycle_t *cycle);\n\nextern ngx_bpf_program_t  ngx_quic_reuseport_helper;\n\n\nstatic ngx_command_t  ngx_quic_bpf_commands[] = {\n\n    { ngx_string(\"quic_bpf\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_quic_bpf_conf_t, enabled),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_quic_bpf_module_ctx = {\n    ngx_string(\"quic_bpf\"),\n    ngx_quic_bpf_create_conf,\n    NULL\n};\n\n\nngx_module_t  ngx_quic_bpf_module = {\n    NGX_MODULE_V1,\n    &ngx_quic_bpf_module_ctx,              /* module context */\n    ngx_quic_bpf_commands,                 /* module directives */\n    NGX_CORE_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    ngx_quic_bpf_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 void *\nngx_quic_bpf_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_quic_bpf_conf_t  *bcf;\n\n    bcf = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_bpf_conf_t));\n    if (bcf == NULL) {\n        return NULL;\n    }\n\n    bcf->enabled = NGX_CONF_UNSET;\n    bcf->map_size = NGX_CONF_UNSET_UINT;\n\n    ngx_queue_init(&bcf->groups);\n\n    return bcf;\n}\n\n\nstatic ngx_int_t\nngx_quic_bpf_module_init(ngx_cycle_t *cycle)\n{\n    ngx_uint_t            i;\n    ngx_listening_t      *ls;\n    ngx_core_conf_t      *ccf;\n    ngx_pool_cleanup_t   *cln;\n    ngx_quic_bpf_conf_t  *bcf;\n\n    if (ngx_test_config) {\n        return NGX_OK;\n    }\n\n    ccf = ngx_core_get_conf(cycle);\n    bcf = ngx_quic_bpf_get_conf(cycle);\n\n    ngx_conf_init_value(bcf->enabled, 0);\n\n    bcf->map_size = ccf->worker_processes * 4;\n\n    cln = ngx_pool_cleanup_add(cycle->pool, 0);\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    cln->data = bcf;\n    cln->handler = ngx_quic_bpf_cleanup;\n\n    if (ngx_inherited && ngx_is_init_cycle(cycle->old_cycle)) {\n        if (ngx_quic_bpf_import_maps(cycle) != NGX_OK) {\n            goto failed;\n        }\n    }\n\n    ls = cycle->listening.elts;\n\n    for (i = 0; i < cycle->listening.nelts; i++) {\n        if (ls[i].quic && ls[i].reuseport) {\n            if (ngx_quic_bpf_group_add_socket(cycle, &ls[i]) != NGX_OK) {\n                goto failed;\n            }\n        }\n    }\n\n    if (ngx_quic_bpf_export_maps(cycle) != NGX_OK) {\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    if (ngx_is_init_cycle(cycle->old_cycle)) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"ngx_quic_bpf_module failed to initialize, check limits\");\n\n        /* refuse to start */\n        return NGX_ERROR;\n    }\n\n    /*\n     * returning error now will lead to master process exiting immediately\n     * leaving worker processes orphaned, what is really unexpected.\n     * Instead, just issue a not about failed initialization and try\n     * to cleanup a bit. Still program can be already loaded to kernel\n     * for some reuseport groups, and there is no way to revert, so\n     * behaviour may be inconsistent.\n     */\n\n    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                  \"ngx_quic_bpf_module failed to initialize properly, ignored.\"\n                  \"please check limits and note that nginx state now \"\n                  \"can be inconsistent and restart may be required\");\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_quic_bpf_cleanup(void *data)\n{\n    ngx_quic_bpf_conf_t  *bcf = (ngx_quic_bpf_conf_t *) data;\n\n    ngx_queue_t            *q;\n    ngx_quic_sock_group_t  *grp;\n\n    for (q = ngx_queue_head(&bcf->groups);\n         q != ngx_queue_sentinel(&bcf->groups);\n         q = ngx_queue_next(q))\n    {\n        grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);\n\n        ngx_quic_bpf_close(ngx_cycle->log, grp->map_fd, \"map\");\n        ngx_quic_bpf_close(ngx_cycle->log, grp->map_active_fd, \"active_map\");\n    }\n}\n\n\nstatic ngx_inline void\nngx_quic_bpf_close(ngx_log_t *log, int fd, const char *name)\n{\n    if (close(fd) != -1) {\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                  \"quic bpf close %s fd:%d failed\", name, fd);\n}\n\n\nstatic ngx_quic_sock_group_t *\nngx_quic_bpf_find_group(ngx_quic_bpf_conf_t *bcf, ngx_listening_t *ls)\n{\n    ngx_queue_t            *q;\n    ngx_quic_sock_group_t  *grp;\n\n    for (q = ngx_queue_head(&bcf->groups);\n         q != ngx_queue_sentinel(&bcf->groups);\n         q = ngx_queue_next(q))\n    {\n        grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);\n\n        if (ngx_cmp_sockaddr(ls->sockaddr, ls->socklen,\n                             grp->sockaddr, grp->socklen, 1)\n            == NGX_OK)\n        {\n            return grp;\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_quic_sock_group_t *\nngx_quic_bpf_alloc_group(ngx_cycle_t *cycle, struct sockaddr *sa,\n    socklen_t socklen)\n{\n    ngx_quic_bpf_conf_t    *bcf;\n    ngx_quic_sock_group_t  *grp;\n\n    bcf = ngx_quic_bpf_get_conf(cycle);\n\n    grp = ngx_pcalloc(cycle->pool, sizeof(ngx_quic_sock_group_t));\n    if (grp == NULL) {\n        return NULL;\n    }\n\n    grp->map_fd = -1;\n    grp->map_active_fd = -1;\n\n    grp->socklen = socklen;\n    grp->sockaddr = ngx_palloc(cycle->pool, socklen);\n    if (grp->sockaddr == NULL) {\n        return NULL;\n    }\n    ngx_memcpy(grp->sockaddr, sa, socklen);\n\n    ngx_queue_insert_tail(&bcf->groups, &grp->queue);\n\n    return grp;\n}\n\n\nstatic ngx_quic_sock_group_t *\nngx_quic_bpf_create_group(ngx_cycle_t *cycle, ngx_listening_t *ls)\n{\n    int                     progfd, failed, flags, rc;\n    ngx_quic_bpf_conf_t    *bcf;\n    ngx_quic_sock_group_t  *grp;\n\n    bcf = ngx_quic_bpf_get_conf(cycle);\n\n    if (!bcf->enabled) {\n        return NULL;\n    }\n\n    grp = ngx_quic_bpf_alloc_group(cycle, ls->sockaddr, ls->socklen);\n    if (grp == NULL) {\n        return NULL;\n    }\n\n    grp->map_fd = ngx_bpf_map_create(cycle->log, BPF_MAP_TYPE_SOCKHASH,\n                                     sizeof(uint64_t), sizeof(uint64_t),\n                                     bcf->map_size, 0);\n    if (grp->map_fd == -1) {\n        goto failed;\n    }\n\n    flags = fcntl(grp->map_fd, F_GETFD);\n    if (flags == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, errno,\n                      \"quic bpf getfd failed\");\n        goto failed;\n    }\n\n    /* need to inherit map during binary upgrade after exec */\n    flags &= ~FD_CLOEXEC;\n\n    rc = fcntl(grp->map_fd, F_SETFD, flags);\n    if (rc == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, errno,\n                      \"quic bpf setfd failed\");\n        goto failed;\n    }\n\n    ngx_bpf_program_link(&ngx_quic_reuseport_helper,\n                         \"ngx_quic_sockmap\", grp->map_fd);\n\n    grp->map_active_fd = ngx_bpf_map_create(cycle->log, BPF_MAP_TYPE_SOCKHASH,\n                                     sizeof(uint32_t), sizeof(uint64_t),\n                                     NGX_QUIC_BPF_ACTIVE_KEYS_MAX, 0);\n    if (grp->map_active_fd == -1) {\n        goto failed;\n    }\n\n    flags = fcntl(grp->map_active_fd, F_GETFD);\n    if (flags == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, errno,\n                      \"quic bpf getfd failed\");\n        goto failed;\n    }\n\n    /* need to inherit map during binary upgrade after exec */\n    flags &= ~FD_CLOEXEC;\n\n    rc = fcntl(grp->map_active_fd, F_SETFD, flags);\n    if (rc == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, errno,\n                      \"quic bpf setfd failed\");\n        goto failed;\n    }\n\n    ngx_bpf_program_link(&ngx_quic_reuseport_helper,\n                         \"ngx_quic_sockmap_active\", grp->map_active_fd);\n\n    progfd = ngx_bpf_load_program(cycle->log, &ngx_quic_reuseport_helper);\n    if (progfd < 0) {\n        goto failed;\n    }\n\n    failed = 0;\n\n    if (setsockopt(ls->fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF,\n                   &progfd, sizeof(int))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,\n                      \"quic bpf setsockopt(SO_ATTACH_REUSEPORT_EBPF) failed\");\n        failed = 1;\n    }\n\n    ngx_quic_bpf_close(cycle->log, progfd, \"program\");\n\n    if (failed) {\n        goto failed;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"quic bpf sockmap created fd:%d\", grp->map_fd);\n    return grp;\n\nfailed:\n\n    if (grp->map_fd != -1) {\n        ngx_quic_bpf_close(cycle->log, grp->map_fd, \"map\");\n    }\n\n    if (grp->map_active_fd != -1) {\n        ngx_quic_bpf_close(cycle->log, grp->map_active_fd, \"map_active\");\n    }\n\n    ngx_queue_remove(&grp->queue);\n\n    return NULL;\n}\n\n\nstatic ngx_quic_sock_group_t *\nngx_quic_bpf_get_group(ngx_cycle_t *cycle, ngx_listening_t *ls)\n{\n    ngx_quic_bpf_conf_t    *bcf, *old_bcf;\n    ngx_quic_sock_group_t  *grp, *ogrp;\n\n    bcf = ngx_quic_bpf_get_conf(cycle);\n\n    grp = ngx_quic_bpf_find_group(bcf, ls);\n    if (grp) {\n        return grp;\n    }\n\n    old_bcf = ngx_quic_bpf_get_old_conf(cycle);\n\n    if (old_bcf == NULL) {\n        return ngx_quic_bpf_create_group(cycle, ls);\n    }\n\n    ogrp = ngx_quic_bpf_find_group(old_bcf, ls);\n    if (ogrp == NULL) {\n        return ngx_quic_bpf_create_group(cycle, ls);\n    }\n\n    grp = ngx_quic_bpf_alloc_group(cycle, ls->sockaddr, ls->socklen);\n    if (grp == NULL) {\n        return NULL;\n    }\n\n    grp->map_fd = dup(ogrp->map_fd);\n    if (grp->map_fd == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"quic bpf failed to duplicate bpf map descriptor\");\n\n        ngx_queue_remove(&grp->queue);\n\n        return NULL;\n    }\n\n    grp->map_active_fd = dup(ogrp->map_active_fd);\n    if (grp->map_active_fd == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"quic bpf failed to duplicate bpf map_active descriptor\");\n\n        ngx_queue_remove(&grp->queue);\n\n        return NULL;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"quic bpf sockmap fd duplicated old:%d new:%d\",\n                   ogrp->map_fd, grp->map_fd);\n\n    return grp;\n}\n\n\nstatic ngx_int_t\nngx_quic_bpf_group_add_socket(ngx_cycle_t *cycle,  ngx_listening_t *ls)\n{\n    uint64_t                cookie;\n    ngx_quic_bpf_conf_t    *bcf;\n    ngx_quic_sock_group_t  *grp;\n    ngx_core_conf_t        *ccf;\n    ngx_uint_t              i;\n\n    ccf = ngx_core_get_conf(cycle);\n    bcf = ngx_quic_bpf_get_conf(cycle);\n    grp = ngx_quic_bpf_get_group(cycle, ls);\n\n    if (grp == NULL) {\n        if (!bcf->enabled) {\n            return NGX_OK;\n        }\n\n        return NGX_ERROR;\n    }\n\n    grp->unused = 0;\n\n    cookie = ngx_quic_bpf_socket_key(ls->fd, cycle->log);\n    if (cookie == (uint64_t) NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    /* map[cookie] = socket; for use in kernel helper */\n    if (ngx_bpf_map_update(grp->map_fd, &cookie, &ls->fd, BPF_ANY) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"quic bpf failed to update socket map key=%xL\", cookie);\n        return NGX_ERROR;\n    }\n\n    for (i = ls->worker; i < NGX_QUIC_BPF_ACTIVE_KEYS_MAX; i += ccf->worker_processes) {\n        if (ngx_bpf_map_update(grp->map_active_fd, &i, &ls->fd, BPF_ANY) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                            \"quic bpf failed to update socket map_active key=%d\", i);\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                 \"quic bpf sockmap fd:%d add socket:%d cookie:0x%xL worker:%ui\",\n                 grp->map_fd, ls->fd, cookie, ls->worker);\n\n    /* do not inherit this socket */\n    ls->ignore = 1;\n\n    return NGX_OK;\n}\n\n\nstatic uint64_t\nngx_quic_bpf_socket_key(ngx_fd_t fd, ngx_log_t *log)\n{\n    uint64_t   cookie;\n    socklen_t  optlen;\n\n    optlen = sizeof(cookie);\n\n    if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &optlen) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                      \"quic bpf getsockopt(SO_COOKIE) failed\");\n\n        return (ngx_uint_t) NGX_ERROR;\n    }\n\n    return cookie;\n}\n\n\nstatic ngx_int_t\nngx_quic_bpf_export_maps(ngx_cycle_t *cycle)\n{\n    u_char                 *p, *buf;\n    size_t                  len;\n    ngx_str_t              *var;\n    ngx_queue_t            *q;\n    ngx_core_conf_t        *ccf;\n    ngx_quic_bpf_conf_t    *bcf;\n    ngx_quic_sock_group_t  *grp;\n\n    ccf = ngx_core_get_conf(cycle);\n    bcf = ngx_quic_bpf_get_conf(cycle);\n\n    len = sizeof(NGX_QUIC_BPF_VARNAME) + 1;\n\n    q = ngx_queue_head(&bcf->groups);\n\n    while (q != ngx_queue_sentinel(&bcf->groups)) {\n\n        grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);\n\n        q = ngx_queue_next(q);\n\n        if (grp->unused) {\n            /*\n             * map was inherited, but it is not used in this configuration;\n             * do not pass such map further and drop the group to prevent\n             * interference with changes during reload\n             */\n\n            ngx_quic_bpf_close(cycle->log, grp->map_fd, \"map\");\n            ngx_quic_bpf_close(cycle->log, grp->map_active_fd, \"map_active\");\n            ngx_queue_remove(&grp->queue);\n\n            continue;\n        }\n\n        len += NGX_INT32_LEN + 1 + NGX_SOCKADDR_STRLEN + 1;\n    }\n\n    len++;\n\n    buf = ngx_palloc(cycle->pool, len);\n    if (buf == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(buf, NGX_QUIC_BPF_VARNAME \"=\",\n                   sizeof(NGX_QUIC_BPF_VARNAME));\n\n    for (q = ngx_queue_head(&bcf->groups);\n         q != ngx_queue_sentinel(&bcf->groups);\n         q = ngx_queue_next(q))\n    {\n        grp = ngx_queue_data(q, ngx_quic_sock_group_t, queue);\n\n        p = ngx_sprintf(p, \"%ud%c%ud\", grp->map_active_fd, NGX_QUIC_BPF_FDSEP, grp->map_fd);\n\n        *p++ = NGX_QUIC_BPF_ADDRSEP;\n\n        p += ngx_sock_ntop(grp->sockaddr, grp->socklen, p,\n                           NGX_SOCKADDR_STRLEN, 1);\n\n        *p++ = NGX_QUIC_BPF_VARSEP;\n    }\n\n    *p = '\\0';\n\n    var = ngx_array_push(&ccf->env);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->data = buf;\n    var->len = sizeof(NGX_QUIC_BPF_VARNAME) - 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_bpf_import_maps(ngx_cycle_t *cycle)\n{\n    int                     s, sa;\n    u_char                 *inherited, *p, *v;\n    ngx_uint_t              in_fd;\n    ngx_addr_t              tmp;\n    ngx_quic_bpf_conf_t    *bcf;\n    ngx_quic_sock_group_t  *grp;\n\n    inherited = (u_char *) getenv(NGX_QUIC_BPF_VARNAME);\n\n    if (inherited == NULL) {\n        return NGX_OK;\n    }\n\n    bcf = ngx_quic_bpf_get_conf(cycle);\n\n#if (NGX_SUPPRESS_WARN)\n    s = -1;\n    sa = -1;\n#endif\n\n    in_fd = 1;\n\n    for (p = inherited, v = p; *p; p++) {\n\n        switch (*p) {\n\n        case NGX_QUIC_BPF_FDSEP:\n\n            if (!in_fd) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                              \"quic bpf failed to parse inherited env\");\n                return NGX_ERROR;\n            }\n\n            sa = ngx_atoi(v, p - v);\n            if (sa == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                              \"quic bpf failed to parse inherited map fd\");\n                return NGX_ERROR;\n            }\n\n            v = p + 1;\n            break;\n\n        case NGX_QUIC_BPF_ADDRSEP:\n\n            if (!in_fd) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                              \"quic bpf failed to parse inherited env\");\n                return NGX_ERROR;\n            }\n            in_fd = 0;\n\n            s = ngx_atoi(v, p - v);\n            if (s == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                              \"quic bpf failed to parse inherited map fd\");\n                return NGX_ERROR;\n            }\n\n            v = p + 1;\n            break;\n\n        case NGX_QUIC_BPF_VARSEP:\n\n            if (in_fd) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                              \"quic bpf failed to parse inherited env\");\n                return NGX_ERROR;\n            }\n            in_fd = 1;\n\n            grp = ngx_pcalloc(cycle->pool,\n                              sizeof(ngx_quic_sock_group_t));\n            if (grp == NULL) {\n                return NGX_ERROR;\n            }\n\n            grp->map_fd = s;\n            grp->map_active_fd = sa;\n\n            if (ngx_parse_addr_port(cycle->pool, &tmp, v, p - v)\n                != NGX_OK)\n            {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                              \"quic bpf failed to parse inherited\"\n                              \" address '%*s'\", p - v , v);\n\n                ngx_quic_bpf_close(cycle->log, s, \"inherited map\");\n                ngx_quic_bpf_close(cycle->log, sa, \"inherited active_map\");\n\n                return NGX_ERROR;\n            }\n\n            grp->sockaddr = tmp.sockaddr;\n            grp->socklen = tmp.socklen;\n\n            grp->unused = 1;\n\n            ngx_queue_insert_tail(&bcf->groups, &grp->queue);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"quic bpf sockmap inherited with \"\n                           \"fd:%d address:%*s\",\n                           grp->map_fd, p - v, v);\n            v = p + 1;\n            break;\n\n        default:\n            break;\n        }\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_bpf_code.c",
    "content": "/* AUTO-GENERATED, DO NOT EDIT. */\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include \"ngx_bpf.h\"\n\n\nstatic ngx_bpf_reloc_t bpf_reloc_prog_ngx_quic_reuseport_helper[] = {\n    { \"ngx_quic_sockmap\", 57 },\n    { \"ngx_quic_sockmap_active\", 70 },\n};\n\nstatic struct bpf_insn bpf_insn_prog_ngx_quic_reuseport_helper[] = {\n    /* opcode dst          src         offset imm */\n    { 0xbf,   BPF_REG_6,   BPF_REG_1, (int16_t)      0,        0x0 },\n    { 0x79,   BPF_REG_3,   BPF_REG_6, (int16_t)      0,        0x0 },\n    { 0x79,   BPF_REG_2,   BPF_REG_6, (int16_t)      8,        0x0 },\n    { 0xbf,   BPF_REG_1,   BPF_REG_3, (int16_t)      0,        0x0 },\n    {  0x7,   BPF_REG_1,   BPF_REG_0, (int16_t)      0,        0x8 },\n    { 0x2d,   BPF_REG_1,   BPF_REG_2, (int16_t)     58,        0x0 },\n    { 0xbf,   BPF_REG_4,   BPF_REG_3, (int16_t)      0,        0x0 },\n    {  0x7,   BPF_REG_4,   BPF_REG_0, (int16_t)      0,        0x9 },\n    { 0x2d,   BPF_REG_4,   BPF_REG_2, (int16_t)     55,        0x0 },\n    { 0xb7,   BPF_REG_4,   BPF_REG_0, (int16_t)      0,       0x14 },\n    { 0xb7,   BPF_REG_5,   BPF_REG_0, (int16_t)      0,        0x9 },\n    { 0x71,   BPF_REG_0,   BPF_REG_1, (int16_t)      0,        0x0 },\n    { 0x67,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,       0x38 },\n    { 0xc7,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,       0x38 },\n    { 0x65,   BPF_REG_0,   BPF_REG_0, (int16_t)     10, 0xffffffff },\n    { 0xbf,   BPF_REG_1,   BPF_REG_3, (int16_t)      0,        0x0 },\n    {  0x7,   BPF_REG_1,   BPF_REG_0, (int16_t)      0,        0xd },\n    { 0x2d,   BPF_REG_1,   BPF_REG_2, (int16_t)     46,        0x0 },\n    { 0xbf,   BPF_REG_4,   BPF_REG_3, (int16_t)      0,        0x0 },\n    {  0x7,   BPF_REG_4,   BPF_REG_0, (int16_t)      0,        0xe },\n    { 0x2d,   BPF_REG_4,   BPF_REG_2, (int16_t)     43,        0x0 },\n    { 0xb7,   BPF_REG_5,   BPF_REG_0, (int16_t)      0,        0xe },\n    { 0x71,   BPF_REG_4,   BPF_REG_1, (int16_t)      0,        0x0 },\n    { 0xb7,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,        0x8 },\n    { 0x2d,   BPF_REG_0,   BPF_REG_4, (int16_t)     39,        0x0 },\n    {  0xf,   BPF_REG_4,   BPF_REG_5, (int16_t)      0,        0x0 },\n    {  0xf,   BPF_REG_3,   BPF_REG_4, (int16_t)      0,        0x0 },\n    { 0x2d,   BPF_REG_3,   BPF_REG_2, (int16_t)     36,        0x0 },\n    { 0xbf,   BPF_REG_3,   BPF_REG_1, (int16_t)      0,        0x0 },\n    {  0x7,   BPF_REG_3,   BPF_REG_0, (int16_t)      0,        0x9 },\n    { 0x2d,   BPF_REG_3,   BPF_REG_2, (int16_t)     33,        0x0 },\n    { 0x71,   BPF_REG_3,   BPF_REG_1, (int16_t)      1,        0x0 },\n    { 0x67,   BPF_REG_3,   BPF_REG_0, (int16_t)      0,       0x38 },\n    { 0x71,   BPF_REG_2,   BPF_REG_1, (int16_t)      2,        0x0 },\n    { 0x67,   BPF_REG_2,   BPF_REG_0, (int16_t)      0,       0x30 },\n    { 0x4f,   BPF_REG_2,   BPF_REG_3, (int16_t)      0,        0x0 },\n    { 0x71,   BPF_REG_3,   BPF_REG_1, (int16_t)      3,        0x0 },\n    { 0x67,   BPF_REG_3,   BPF_REG_0, (int16_t)      0,       0x28 },\n    { 0x4f,   BPF_REG_2,   BPF_REG_3, (int16_t)      0,        0x0 },\n    { 0x71,   BPF_REG_3,   BPF_REG_1, (int16_t)      4,        0x0 },\n    { 0x67,   BPF_REG_3,   BPF_REG_0, (int16_t)      0,       0x20 },\n    { 0x4f,   BPF_REG_2,   BPF_REG_3, (int16_t)      0,        0x0 },\n    { 0x71,   BPF_REG_3,   BPF_REG_1, (int16_t)      5,        0x0 },\n    { 0x67,   BPF_REG_3,   BPF_REG_0, (int16_t)      0,       0x18 },\n    { 0x4f,   BPF_REG_2,   BPF_REG_3, (int16_t)      0,        0x0 },\n    { 0x71,   BPF_REG_3,   BPF_REG_1, (int16_t)      6,        0x0 },\n    { 0x67,   BPF_REG_3,   BPF_REG_0, (int16_t)      0,       0x10 },\n    { 0x4f,   BPF_REG_2,   BPF_REG_3, (int16_t)      0,        0x0 },\n    { 0x71,   BPF_REG_3,   BPF_REG_1, (int16_t)      7,        0x0 },\n    { 0x67,   BPF_REG_3,   BPF_REG_0, (int16_t)      0,        0x8 },\n    { 0x4f,   BPF_REG_2,   BPF_REG_3, (int16_t)      0,        0x0 },\n    { 0x71,   BPF_REG_1,   BPF_REG_1, (int16_t)      8,        0x0 },\n    { 0x4f,   BPF_REG_2,   BPF_REG_1, (int16_t)      0,        0x0 },\n    { 0x7b,  BPF_REG_10,   BPF_REG_2, (int16_t)  65528,        0x0 },\n    { 0xbf,   BPF_REG_3,  BPF_REG_10, (int16_t)      0,        0x0 },\n    {  0x7,   BPF_REG_3,   BPF_REG_0, (int16_t)      0, 0xfffffff8 },\n    { 0xbf,   BPF_REG_1,   BPF_REG_6, (int16_t)      0,        0x0 },\n    { 0x18,   BPF_REG_2,   BPF_REG_0, (int16_t)      0,        0x0 },\n    {  0x0,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,        0x0 },\n    { 0xb7,   BPF_REG_4,   BPF_REG_0, (int16_t)      0,        0x0 },\n    { 0x85,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,       0x52 },\n    { 0x67,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,       0x20 },\n    { 0x77,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,       0x20 },\n    { 0x15,   BPF_REG_0,   BPF_REG_0, (int16_t)     10,        0x0 },\n    { 0x61,   BPF_REG_1,   BPF_REG_6, (int16_t)     32,        0x0 },\n    { 0x57,   BPF_REG_1,   BPF_REG_0, (int16_t)      0,       0xff },\n    { 0x7b,  BPF_REG_10,   BPF_REG_1, (int16_t)  65528,        0x0 },\n    { 0xbf,   BPF_REG_3,  BPF_REG_10, (int16_t)      0,        0x0 },\n    {  0x7,   BPF_REG_3,   BPF_REG_0, (int16_t)      0, 0xfffffff8 },\n    { 0xbf,   BPF_REG_1,   BPF_REG_6, (int16_t)      0,        0x0 },\n    { 0x18,   BPF_REG_2,   BPF_REG_0, (int16_t)      0,        0x0 },\n    {  0x0,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,        0x0 },\n    { 0xb7,   BPF_REG_4,   BPF_REG_0, (int16_t)      0,        0x0 },\n    { 0x85,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,       0x52 },\n    { 0xb7,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,        0x1 },\n    { 0x95,   BPF_REG_0,   BPF_REG_0, (int16_t)      0,        0x0 },\n};\n\n\nngx_bpf_program_t ngx_quic_reuseport_helper = {\n    .relocs = bpf_reloc_prog_ngx_quic_reuseport_helper,\n    .nrelocs = sizeof(bpf_reloc_prog_ngx_quic_reuseport_helper)\n               / sizeof(bpf_reloc_prog_ngx_quic_reuseport_helper[0]),\n    .ins = bpf_insn_prog_ngx_quic_reuseport_helper,\n    .nins = sizeof(bpf_insn_prog_ngx_quic_reuseport_helper)\n            / sizeof(bpf_insn_prog_ngx_quic_reuseport_helper[0]),\n    .license = \"BSD\",\n    .type = BPF_PROG_TYPE_SK_REUSEPORT,\n};\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_connection.h",
    "content": "/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_\n#define _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n/* #define NGX_QUIC_DEBUG_PACKETS */  /* dump packet contents */\n/* #define NGX_QUIC_DEBUG_FRAMES */   /* dump frames contents */\n/* #define NGX_QUIC_DEBUG_ALLOC */    /* log frames and bufs alloc */\n/* #define NGX_QUIC_DEBUG_CRYPTO */\n\ntypedef struct ngx_quic_connection_s  ngx_quic_connection_t;\ntypedef struct ngx_quic_server_id_s   ngx_quic_server_id_t;\ntypedef struct ngx_quic_client_id_s   ngx_quic_client_id_t;\ntypedef struct ngx_quic_send_ctx_s    ngx_quic_send_ctx_t;\ntypedef struct ngx_quic_socket_s      ngx_quic_socket_t;\ntypedef struct ngx_quic_path_s        ngx_quic_path_t;\ntypedef struct ngx_quic_keys_s        ngx_quic_keys_t;\n\n#include <ngx_event_quic_transport.h>\n#include <ngx_event_quic_protection.h>\n#include <ngx_event_quic_frames.h>\n#include <ngx_event_quic_migration.h>\n#include <ngx_event_quic_connid.h>\n#include <ngx_event_quic_streams.h>\n#include <ngx_event_quic_ssl.h>\n#include <ngx_event_quic_tokens.h>\n#include <ngx_event_quic_ack.h>\n#include <ngx_event_quic_output.h>\n#include <ngx_event_quic_socket.h>\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n#include <ngx_event_quic_mtu.h>\n#endif\n\n\n/* RFC 9002, 6.2.2.  Handshakes and New Paths: kInitialRtt */\n#define NGX_QUIC_INITIAL_RTT                 333 /* ms */\n\n#define NGX_QUIC_UNSET_PN                    (uint64_t) -1\n\n#define NGX_QUIC_SEND_CTX_LAST               (NGX_QUIC_ENCRYPTION_LAST - 1)\n\n/*  0-RTT and 1-RTT data exist in the same packet number space,\n *  so we have 3 packet number spaces:\n *\n *  0 - Initial\n *  1 - Handshake\n *  2 - 0-RTT and 1-RTT\n */\n#define ngx_quic_get_send_ctx(qc, level)                                      \\\n    ((level) == ssl_encryption_initial) ? &((qc)->send_ctx[0])                \\\n        : (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1])       \\\n                                                 : &((qc)->send_ctx[2]))\n\n#define ngx_quic_get_connection(c)                                            \\\n    (((c)->udp) ? (((ngx_quic_socket_t *)((c)->udp))->quic) : NULL)\n\n#define ngx_quic_get_socket(c)               ((ngx_quic_socket_t *)((c)->udp))\n\n\nstruct ngx_quic_client_id_s {\n    ngx_queue_t                       queue;\n    uint64_t                          seqnum;\n    size_t                            len;\n    u_char                            id[NGX_QUIC_CID_LEN_MAX];\n    u_char                            sr_token[NGX_QUIC_SR_TOKEN_LEN];\n    ngx_uint_t                        refcnt;\n};\n\n\nstruct ngx_quic_server_id_s {\n    uint64_t                          seqnum;\n    size_t                            len;\n    u_char                            id[NGX_QUIC_CID_LEN_MAX];\n};\n\n\nstruct ngx_quic_path_s {\n    ngx_queue_t                       queue;\n    struct sockaddr                  *sockaddr;\n    socklen_t                         socklen;\n    ngx_uint_t                        state;\n    ngx_msec_t                        expires;\n    ngx_uint_t                        tries;\n    off_t                             sent;\n    off_t                             received;\n    u_char                            challenge1[8];\n    u_char                            challenge2[8];\n    ngx_uint_t                        refcnt;\n    uint64_t                          seqnum;\n    time_t                            validated_at;\n    ngx_str_t                         addr_text;\n    u_char                            text[NGX_SOCKADDR_STRLEN];\n};\n\n\nstruct ngx_quic_socket_s {\n    ngx_udp_connection_t              udp;\n    ngx_quic_connection_t            *quic;\n    ngx_queue_t                       queue;\n\n    ngx_quic_server_id_t              sid;\n\n    ngx_quic_path_t                  *path;\n    ngx_quic_client_id_t             *cid;\n};\n\n\ntypedef struct {\n    ngx_rbtree_t                      tree;\n    ngx_rbtree_node_t                 sentinel;\n    ngx_queue_t                       uninitialized;\n\n    uint64_t                          sent;\n    uint64_t                          exemptions;\n    uint64_t                          recv_offset;\n    uint64_t                          recv_window;\n    uint64_t                          recv_last;\n    uint64_t                          recv_max_data;\n    uint64_t                          send_max_data;\n\n    uint64_t                          server_max_streams_uni;\n    uint64_t                          server_max_streams_bidi;\n    uint64_t                          server_streams_uni;\n    uint64_t                          server_streams_bidi;\n\n    uint64_t                          client_max_streams_uni;\n    uint64_t                          client_max_streams_bidi;\n    uint64_t                          client_streams_uni;\n    uint64_t                          client_streams_bidi;\n\n    ngx_uint_t                        initialized;\n                                                 /* unsigned  initialized:1; */\n} ngx_quic_streams_t;\n\n\ntypedef struct {\n    size_t                            in_flight;\n    size_t                            window;\n    size_t                            ssthresh;\n    ngx_msec_t                        recovery_start;\n} ngx_quic_congestion_t;\n\n\n/*\n * RFC 9000, 12.3.  Packet Numbers\n *\n *  Conceptually, a packet number space is the context in which a packet\n *  can be processed and acknowledged.  Initial packets can only be sent\n *  with Initial packet protection keys and acknowledged in packets that\n *  are also Initial packets.\n */\nstruct ngx_quic_send_ctx_s {\n    enum ssl_encryption_level_t       level;\n\n    ngx_chain_t                      *crypto;\n    uint64_t                          crypto_received;\n    uint64_t                          crypto_sent;\n\n    uint64_t                          pnum;        /* to be sent */\n    uint64_t                          largest_ack; /* received from peer */\n    uint64_t                          largest_pn;  /* received from peer */\n\n    ngx_queue_t                       frames;      /* generated frames */\n    ngx_quic_fqueue_t                 fqueue;\n\n    ngx_queue_t                       fqueues;     /* generated frames by queues */\n    ngx_queue_t                       sending;     /* frames assigned to pkt */\n    ngx_queue_t                       sent;        /* frames waiting ACK */\n\n    ngx_quic_frame_t                 *last_priority;\n\n    uint64_t                          pending_ack; /* non sent ack-eliciting */\n    uint64_t                          largest_range;\n    uint64_t                          first_range;\n    ngx_msec_t                        largest_received;\n    ngx_msec_t                        ack_delay_start;\n    ngx_uint_t                        nranges;\n    ngx_quic_ack_range_t              ranges[NGX_QUIC_MAX_RANGES];\n    ngx_uint_t                        send_ack;\n\n    unsigned                          ack_immediately:1;\n};\n\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\ntypedef struct {\n    size_t                            min_probe_length;\n    size_t                            max_probe_length;\n    size_t                            last_probe_length;\n\n    ngx_int_t                         remaining_probe_count;\n\n    /* The number of packets between MTU probes. */\n    uint64_t                          packets_between_probes;\n\n    /* The packet number of the packet after which the next MTU probe will be sent. */\n    uint64_t                          next_probe_at;\n\n    unsigned                          process:1;\n} ngx_quic_mtu_t;\n#endif\n\n\nstruct ngx_quic_connection_s {\n    uint32_t                          version;\n\n    ngx_quic_socket_t                *socket;\n    ngx_quic_socket_t                *backup;\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    ngx_quic_mtu_t                    mtu;\n#endif\n\n    ngx_queue_t                       sockets;\n    ngx_queue_t                       paths;\n    ngx_queue_t                       client_ids;\n    ngx_queue_t                       free_sockets;\n    ngx_queue_t                       free_paths;\n    ngx_queue_t                       free_client_ids;\n\n    ngx_uint_t                        nsockets;\n    ngx_uint_t                        nclient_ids;\n    uint64_t                          max_retired_seqnum;\n    uint64_t                          client_seqnum;\n    uint64_t                          server_seqnum;\n    uint64_t                          path_seqnum;\n\n    ngx_quic_tp_t                     tp;\n    ngx_quic_tp_t                     ctp;\n\n    ngx_quic_send_ctx_t               send_ctx[NGX_QUIC_SEND_CTX_LAST];\n\n    ngx_quic_keys_t                  *keys;\n\n    ngx_quic_conf_t                  *conf;\n\n    ngx_event_t                       push;\n    ngx_event_t                       pto;\n    ngx_event_t                       close;\n    ngx_event_t                       path_validation;\n    ngx_msec_t                        last_cc;\n\n    ngx_msec_t                        first_rtt;\n    ngx_msec_t                        latest_rtt;\n    ngx_msec_t                        avg_rtt;\n    ngx_msec_t                        min_rtt;\n    ngx_msec_t                        rttvar;\n\n    ngx_uint_t                        pto_count;\n\n    ngx_queue_t                       free_frames;\n    ngx_chain_t                      *free_bufs;\n    ngx_buf_t                        *free_shadow_bufs;\n\n    ngx_uint_t                        nframes;\n#ifdef NGX_QUIC_DEBUG_ALLOC\n    ngx_uint_t                        nbufs;\n#endif\n\n    ngx_quic_streams_t                streams;\n    ngx_quic_congestion_t             congestion;\n\n    off_t                             received;\n\n    ngx_uint_t                        error;\n    enum ssl_encryption_level_t       error_level;\n    ngx_uint_t                        error_ftype;\n    const char                       *error_reason;\n\n    ngx_uint_t                        shutdown_code;\n    ngx_int_t                         shutdown_rc;\n    const char                       *shutdown_reason;\n\n    unsigned                          error_app:1;\n    unsigned                          send_timer_set:1;\n    unsigned                          closing:1;\n    unsigned                          shutdown:1;\n    unsigned                          draining:1;\n    unsigned                          key_phase:1;\n    unsigned                          validated:1;\n    unsigned                          client_tp_done:1;\n};\n\n\nngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,\n    ngx_quic_tp_t *ctp);\nvoid ngx_quic_discard_ctx(ngx_connection_t *c,\n    enum ssl_encryption_level_t level);\nvoid ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);\nvoid ngx_quic_shutdown_quic(ngx_connection_t *c, ngx_int_t rc);\n\n#if (NGX_DEBUG)\nvoid ngx_quic_connstate_dbg(ngx_connection_t *c);\n#else\n#define ngx_quic_connstate_dbg(c)\n#endif\n\n#endif /* _NGX_EVENT_QUIC_CONNECTION_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_connid.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#include <ngx_event_quic_connection.h>\n\n#define NGX_QUIC_MAX_SERVER_IDS   8\n\n\n#if (NGX_QUIC_BPF)\nstatic ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id);\n#endif\nstatic ngx_int_t ngx_quic_send_retire_connection_id(ngx_connection_t *c,\n    uint64_t seqnum);\n\nstatic ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c,\n    ngx_quic_connection_t *qc);\nstatic ngx_int_t ngx_quic_replace_retired_client_id(ngx_connection_t *c,\n    ngx_quic_client_id_t *retired_cid);\nstatic ngx_int_t ngx_quic_send_server_id(ngx_connection_t *c,\n    ngx_quic_server_id_t *sid);\n\n\nngx_int_t\nngx_quic_create_server_id(ngx_connection_t *c, u_char *id)\n{\n    if (RAND_bytes(id, NGX_QUIC_SERVER_CID_LEN) != 1) {\n        return NGX_ERROR;\n    }\n\n#if (NGX_QUIC_BPF)\n    if (ngx_quic_bpf_attach_id(c, id) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"quic bpf failed to generate socket key\");\n        /* ignore error, things still may work */\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\n#if (NGX_QUIC_BPF)\n\nstatic ngx_int_t\nngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id)\n{\n    int        fd;\n    uint64_t   cookie;\n    socklen_t  optlen;\n\n    fd = c->listening->fd;\n\n    optlen = sizeof(cookie);\n\n    if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &optlen) == -1) {\n        ngx_log_error(NGX_LOG_ERR, c->log, ngx_socket_errno,\n                      \"quic getsockopt(SO_COOKIE) failed\");\n\n        return NGX_ERROR;\n    }\n\n    ngx_quic_dcid_encode_key(id, cookie);\n\n    return NGX_OK;\n}\n\n#endif\n\n\nngx_int_t\nngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,\n    ngx_quic_new_conn_id_frame_t *f)\n{\n    uint64_t                seq;\n    ngx_str_t               id;\n    ngx_queue_t            *q;\n    ngx_quic_client_id_t   *cid, *item;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (f->seqnum < qc->max_retired_seqnum) {\n        /*\n         * RFC 9000, 19.15.  NEW_CONNECTION_ID Frame\n         *\n         *  An endpoint that receives a NEW_CONNECTION_ID frame with\n         *  a sequence number smaller than the Retire Prior To field\n         *  of a previously received NEW_CONNECTION_ID frame MUST send\n         *  a corresponding RETIRE_CONNECTION_ID frame that retires\n         *  the newly received connection ID, unless it has already\n         *  done so for that sequence number.\n         */\n\n        if (ngx_quic_send_retire_connection_id(c, f->seqnum) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        goto retire;\n    }\n\n    cid = NULL;\n\n    for (q = ngx_queue_head(&qc->client_ids);\n         q != ngx_queue_sentinel(&qc->client_ids);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_quic_client_id_t, queue);\n\n        if (item->seqnum == f->seqnum) {\n            cid = item;\n            break;\n        }\n    }\n\n    if (cid) {\n        /*\n         * Transmission errors, timeouts, and retransmissions might cause the\n         * same NEW_CONNECTION_ID frame to be received multiple times.\n         */\n\n        if (cid->len != f->len\n            || ngx_strncmp(cid->id, f->cid, f->len) != 0\n            || ngx_strncmp(cid->sr_token, f->srt, NGX_QUIC_SR_TOKEN_LEN) != 0)\n        {\n            /*\n             * ..if a sequence number is used for different connection IDs,\n             * the endpoint MAY treat that receipt as a connection error\n             * of type PROTOCOL_VIOLATION.\n             */\n            qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;\n            qc->error_reason = \"seqnum refers to different connection id/token\";\n            return NGX_ERROR;\n        }\n\n    } else {\n\n        id.data = f->cid;\n        id.len = f->len;\n\n        if (ngx_quic_create_client_id(c, &id, f->seqnum, f->srt) == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\nretire:\n\n    if (qc->max_retired_seqnum && f->retire <= qc->max_retired_seqnum) {\n        /*\n         * Once a sender indicates a Retire Prior To value, smaller values sent\n         * in subsequent NEW_CONNECTION_ID frames have no effect.  A receiver\n         * MUST ignore any Retire Prior To fields that do not increase the\n         * largest received Retire Prior To value.\n         */\n        goto done;\n    }\n\n    qc->max_retired_seqnum = f->retire;\n\n    q = ngx_queue_head(&qc->client_ids);\n\n    while (q != ngx_queue_sentinel(&qc->client_ids)) {\n\n        cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);\n        q = ngx_queue_next(q);\n\n        if (cid->seqnum >= f->retire) {\n            continue;\n        }\n\n        /* this connection id must be retired */\n        seq = cid->seqnum;\n\n        if (cid->refcnt) {\n            /* we are going to retire client id which is in use */\n            if (ngx_quic_replace_retired_client_id(c, cid) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n        } else {\n            ngx_quic_unref_client_id(c, cid);\n        }\n\n        if (ngx_quic_send_retire_connection_id(c, seq) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\ndone:\n\n    if (qc->nclient_ids > qc->tp.active_connection_id_limit) {\n        /*\n         * RFC 9000, 5.1.1.  Issuing Connection IDs\n         *\n         * After processing a NEW_CONNECTION_ID frame and\n         * adding and retiring active connection IDs, if the number of active\n         * connection IDs exceeds the value advertised in its\n         * active_connection_id_limit transport parameter, an endpoint MUST\n         * close the connection with an error of type CONNECTION_ID_LIMIT_ERROR.\n         */\n        qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;\n        qc->error_reason = \"too many connection ids received\";\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_send_retire_connection_id(ngx_connection_t *c, uint64_t seqnum)\n{\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;\n    frame->u.retire_cid.sequence_number = seqnum;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    /* we are no longer going to use this client id */\n\n    return NGX_OK;\n}\n\n\nstatic ngx_quic_client_id_t *\nngx_quic_alloc_client_id(ngx_connection_t *c, ngx_quic_connection_t *qc)\n{\n    ngx_queue_t           *q;\n    ngx_quic_client_id_t  *cid;\n\n    if (!ngx_queue_empty(&qc->free_client_ids)) {\n\n        q = ngx_queue_head(&qc->free_client_ids);\n        cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);\n\n        ngx_queue_remove(&cid->queue);\n\n        ngx_memzero(cid, sizeof(ngx_quic_client_id_t));\n\n    } else {\n\n        cid = ngx_pcalloc(c->pool, sizeof(ngx_quic_client_id_t));\n        if (cid == NULL) {\n            return NULL;\n        }\n    }\n\n    return cid;\n}\n\n\nngx_quic_client_id_t *\nngx_quic_create_client_id(ngx_connection_t *c, ngx_str_t *id,\n    uint64_t seqnum, u_char *token)\n{\n    ngx_quic_client_id_t   *cid;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    cid = ngx_quic_alloc_client_id(c, qc);\n    if (cid == NULL) {\n        return NULL;\n    }\n\n    cid->seqnum = seqnum;\n\n    cid->len = id->len;\n    ngx_memcpy(cid->id, id->data, id->len);\n\n    if (token) {\n        ngx_memcpy(cid->sr_token, token, NGX_QUIC_SR_TOKEN_LEN);\n    }\n\n    ngx_queue_insert_tail(&qc->client_ids, &cid->queue);\n    qc->nclient_ids++;\n\n    if (seqnum > qc->client_seqnum) {\n        qc->client_seqnum = seqnum;\n    }\n\n    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic cid #%uL received id:%uz:%xV:%*xs\",\n                    cid->seqnum, id->len, id,\n                    (size_t) NGX_QUIC_SR_TOKEN_LEN, cid->sr_token);\n\n    return cid;\n}\n\n\nngx_quic_client_id_t *\nngx_quic_next_client_id(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    ngx_quic_client_id_t   *cid;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    for (q = ngx_queue_head(&qc->client_ids);\n         q != ngx_queue_sentinel(&qc->client_ids);\n         q = ngx_queue_next(q))\n    {\n        cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);\n\n        if (cid->refcnt == 0) {\n            return cid;\n        }\n    }\n\n    return NULL;\n}\n\n\nngx_quic_client_id_t *\nngx_quic_used_client_id(ngx_connection_t *c, ngx_quic_path_t *path)\n{\n    ngx_queue_t            *q;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    /* best guess: cid used by active path is good for us */\n    if (qc->socket->path == path) {\n        return qc->socket->cid;\n    }\n\n    for (q = ngx_queue_head(&qc->sockets);\n         q != ngx_queue_sentinel(&qc->sockets);\n         q = ngx_queue_next(q))\n    {\n        qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);\n\n        if (qsock->path && qsock->path == path) {\n            return qsock->cid;\n        }\n    }\n\n    return NULL;\n}\n\n\nngx_int_t\nngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,\n    ngx_quic_retire_cid_frame_t *f)\n{\n    ngx_quic_path_t        *path;\n    ngx_quic_socket_t      *qsock, **tmp;\n    ngx_quic_client_id_t   *cid;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (f->sequence_number >= qc->server_seqnum) {\n        /*\n         * RFC 9000, 19.16.\n         *\n         *  Receipt of a RETIRE_CONNECTION_ID frame containing a sequence\n         *  number greater than any previously sent to the peer MUST be\n         *  treated as a connection error of type PROTOCOL_VIOLATION.\n         */\n        qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;\n        qc->error_reason = \"sequence number of id to retire was never issued\";\n\n        return NGX_ERROR;\n    }\n\n    qsock = ngx_quic_get_socket(c);\n\n    if (qsock->sid.seqnum == f->sequence_number) {\n\n        /*\n         * RFC 9000, 19.16.\n         *\n         * The sequence number specified in a RETIRE_CONNECTION_ID frame MUST\n         * NOT refer to the Destination Connection ID field of the packet in\n         * which the frame is contained.  The peer MAY treat this as a\n         * connection error of type PROTOCOL_VIOLATION.\n         */\n\n        qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;\n        qc->error_reason = \"sequence number of id to retire refers DCID\";\n\n        return NGX_ERROR;\n    }\n\n    qsock = ngx_quic_find_socket(c, f->sequence_number);\n    if (qsock == NULL) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic socket #%uL is retired\", qsock->sid.seqnum);\n\n    /* check if client is willing to retire sid we have in use */\n    if (qsock->sid.seqnum == qc->socket->sid.seqnum) {\n        tmp = &qc->socket;\n\n    } else if (qc->backup && qsock->sid.seqnum == qc->backup->sid.seqnum) {\n        tmp = &qc->backup;\n\n    } else {\n\n        ngx_quic_close_socket(c, qsock);\n\n        /* restore socket count up to a limit after deletion */\n        if (ngx_quic_create_sockets(c) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    /* preserve path/cid from retired socket */\n    path = qsock->path;\n    cid = qsock->cid;\n\n    /* ensure that closing_socket will not drop path and cid */\n    path->refcnt++;\n    cid->refcnt++;\n\n    ngx_quic_close_socket(c, qsock);\n\n    /* restore original values */\n    path->refcnt--;\n    cid->refcnt--;\n\n    /* restore socket count up to a limit after deletion */\n    if (ngx_quic_create_sockets(c) != NGX_OK) {\n        goto failed;\n    }\n\n    qsock = ngx_quic_get_unconnected_socket(c);\n    if (qsock == NULL) {\n        qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;\n        qc->error_reason = \"not enough server IDs\";\n        goto failed;\n    }\n\n    ngx_quic_connect(c, qsock, path, cid);\n\n    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic %s socket is now #%uL:%uL:%uL (%s)\",\n                   (*tmp) == qc->socket ? \"active\" : \"backup\",\n                   qsock->sid.seqnum, qsock->cid->seqnum,\n                   qsock->path->seqnum,\n                   ngx_quic_path_state_str(qsock->path));\n\n    /* restore active/backup pointer in quic connection */\n    *tmp = qsock;\n\n    return NGX_OK;\n\nfailed:\n\n    /*\n     * socket was closed, path and cid were preserved artifically\n     * to be reused, but it didn't happen, thus unref here\n     */\n\n    ngx_quic_unref_path(c, path);\n    ngx_quic_unref_client_id(c, cid);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_quic_create_sockets(ngx_connection_t *c)\n{\n    ngx_uint_t              n;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    n = ngx_min(NGX_QUIC_MAX_SERVER_IDS, qc->ctp.active_connection_id_limit);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic create sockets has:%ui max:%ui\", qc->nsockets, n);\n\n    while (qc->nsockets < n) {\n\n        qsock = ngx_quic_alloc_socket(c, qc);\n        if (qsock == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_quic_listen(c, qc, qsock) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_quic_send_server_id(c, &qsock->sid) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)\n{\n    ngx_str_t               dcid;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    dcid.len = sid->len;\n    dcid.data = sid->id;\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID;\n    frame->u.ncid.seqnum = sid->seqnum;\n    frame->u.ncid.retire = 0;\n    frame->u.ncid.len = NGX_QUIC_SERVER_CID_LEN;\n    ngx_memcpy(frame->u.ncid.cid, sid->id, NGX_QUIC_SERVER_CID_LEN);\n\n    if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key,\n                              frame->u.ncid.srt)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_quic_queue_frame(qc, frame);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_replace_retired_client_id(ngx_connection_t *c,\n    ngx_quic_client_id_t *retired_cid)\n{\n    ngx_queue_t            *q;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_client_id_t   *cid;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    for (q = ngx_queue_head(&qc->sockets);\n         q != ngx_queue_sentinel(&qc->sockets);\n         q = ngx_queue_next(q))\n    {\n        qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);\n\n        if (qsock->cid == retired_cid) {\n\n            cid = ngx_quic_next_client_id(c);\n            if (cid == NULL) {\n                return NGX_ERROR;\n            }\n\n            qsock->cid = cid;\n            cid->refcnt++;\n\n            ngx_quic_unref_client_id(c, retired_cid);\n\n            if (retired_cid->refcnt == 0) {\n                return NGX_OK;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_unref_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)\n{\n    ngx_quic_connection_t  *qc;\n\n    if (cid->refcnt) {\n        cid->refcnt--;\n    } /* else: unused client id */\n\n    if (cid->refcnt) {\n        return;\n    }\n\n    qc = ngx_quic_get_connection(c);\n\n    ngx_queue_remove(&cid->queue);\n    ngx_queue_insert_head(&qc->free_client_ids, &cid->queue);\n\n    qc->nclient_ids--;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_connid.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_CONNID_H_INCLUDED_\n#define _NGX_EVENT_QUIC_CONNID_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c,\n    ngx_quic_retire_cid_frame_t *f);\nngx_int_t ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,\n    ngx_quic_new_conn_id_frame_t *f);\n\nngx_int_t ngx_quic_create_sockets(ngx_connection_t *c);\nngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id);\n\nngx_quic_client_id_t *ngx_quic_create_client_id(ngx_connection_t *c,\n    ngx_str_t *id, uint64_t seqnum, u_char *token);\nngx_quic_client_id_t *ngx_quic_next_client_id(ngx_connection_t *c);\nngx_quic_client_id_t *ngx_quic_used_client_id(ngx_connection_t *c,\n    ngx_quic_path_t *path);\nvoid ngx_quic_unref_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid);\n\n#endif /* _NGX_EVENT_QUIC_CONNID_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_frames.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#include <ngx_event_quic_connection.h>\n\n\n#define NGX_QUIC_BUFFER_SIZE  4096\n\n\nstatic ngx_chain_t *ngx_quic_split_bufs(ngx_connection_t *c, ngx_chain_t *in,\n    size_t len);\n\n\nngx_quic_frame_t *\nngx_quic_alloc_frame(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (!ngx_queue_empty(&qc->free_frames)) {\n\n        q = ngx_queue_head(&qc->free_frames);\n        frame = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        ngx_queue_remove(&frame->queue);\n\n#ifdef NGX_QUIC_DEBUG_ALLOC\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic reuse frame n:%ui\", qc->nframes);\n#endif\n\n    } else if (qc->nframes < 10000) {\n        frame = ngx_palloc(c->pool, sizeof(ngx_quic_frame_t));\n        if (frame == NULL) {\n            return NULL;\n        }\n\n        ++qc->nframes;\n\n#ifdef NGX_QUIC_DEBUG_ALLOC\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic alloc frame n:%ui\", qc->nframes);\n#endif\n\n    } else {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"quic flood detected\");\n        return NULL;\n    }\n\n    ngx_memzero(frame, sizeof(ngx_quic_frame_t));\n\n    return frame;\n}\n\n\nvoid\nngx_quic_free_frame(ngx_connection_t *c, ngx_quic_frame_t *frame)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (frame->data) {\n        ngx_quic_free_bufs(c, frame->data);\n    }\n\n    ngx_queue_insert_head(&qc->free_frames, &frame->queue);\n\n#ifdef NGX_QUIC_DEBUG_ALLOC\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic free frame n:%ui\", qc->nframes);\n#endif\n}\n\n\nvoid\nngx_quic_trim_bufs(ngx_chain_t *in, size_t size)\n{\n    size_t      n;\n    ngx_buf_t  *b;\n\n    while (in && size > 0) {\n        b = in->buf;\n        n = ngx_min((size_t) (b->last - b->pos), size);\n\n        b->pos += n;\n        size -= n;\n\n        if (b->pos == b->last) {\n            in = in->next;\n        }\n    }\n}\n\n\nvoid\nngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in)\n{\n    ngx_buf_t              *b, *shadow;\n    ngx_chain_t            *cl;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    while (in) {\n#ifdef NGX_QUIC_DEBUG_ALLOC\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic free buffer n:%ui\", qc->nbufs);\n#endif\n\n        cl = in;\n        in = in->next;\n        b = cl->buf;\n\n        if (b->shadow) {\n            if (!b->last_shadow) {\n                b->recycled = 1;\n                ngx_free_chain(c->pool, cl);\n                continue;\n            }\n\n            do {\n                shadow = b->shadow;\n                b->shadow = qc->free_shadow_bufs;\n                qc->free_shadow_bufs = b;\n                b = shadow;\n            } while (b->recycled);\n\n            if (b->shadow) {\n                b->last_shadow = 1;\n                ngx_free_chain(c->pool, cl);\n                continue;\n            }\n\n            cl->buf = b;\n        }\n\n        cl->next = qc->free_bufs;\n        qc->free_bufs = cl;\n    }\n}\n\n\nvoid\nngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames)\n{\n    ngx_queue_t       *q;\n    ngx_quic_frame_t  *f;\n\n    ngx_quic_connection_t  *qc;\n    qc = ngx_quic_get_connection(c);\n\n    do {\n        q = ngx_queue_head(frames);\n\n        if (q == ngx_queue_sentinel(frames)) {\n            break;\n        }\n\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n        ngx_quic_queue_frame_remove(qc, frames, f);\n\n        ngx_quic_free_frame(c, f);\n    } while (1);\n}\n\n\nvoid\nngx_quic_queue_frame_after(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame, ngx_queue_t *queue, ngx_int_t create)\n{\n    ngx_quic_send_ctx_t  *ctx;\n    ctx = ngx_quic_get_send_ctx(qc, frame->level);\n\n    if (create) {\n        frame->len = ngx_quic_create_frame(NULL, frame);\n        /* always succeeds */\n    }\n\n    ngx_queue_insert_after(queue, &frame->queue);\n\n    if (!ctx->fqueue.attached && queue == &ctx->frames) {\n        ngx_queue_insert_head(&ctx->fqueues, &ctx->fqueue.queue);\n        ctx->fqueue.attached = 1;\n    }\n\n    if (qc->closing) {\n        return;\n    }\n\n    ngx_post_event(&qc->push, &ngx_posted_events);\n}\n\n\nvoid\nngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame)\n{\n    ngx_quic_send_ctx_t  *ctx;\n    ctx = ngx_quic_get_send_ctx(qc, frame->level);\n\n    ngx_quic_queue_frame_after(qc, frame, ngx_queue_last(&ctx->frames), 1);\n}\n\n\nvoid\nngx_quic_queue_frame_priority(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame, ngx_int_t create)\n{\n    ngx_quic_send_ctx_t  *ctx;\n    ctx = ngx_quic_get_send_ctx(qc, frame->level);\n\n    if (ctx->last_priority) {\n        ngx_quic_queue_frame_after(qc, frame, &ctx->last_priority->queue, create);\n    } else {\n        ngx_quic_queue_frame_after(qc, frame, &ctx->frames, create);\n    }\n\n    ctx->last_priority = frame;\n}\n\n\nvoid\nngx_quic_queue_frame_remove(ngx_quic_connection_t *qc, ngx_queue_t *queue, ngx_quic_frame_t *frame)\n{\n    ngx_quic_send_ctx_t  *ctx;\n    ngx_queue_t          *prev;\n\n    ctx = ngx_quic_get_send_ctx(qc, frame->level);\n\n    if (&ctx->frames == queue && ctx->last_priority == frame) {\n        prev = ngx_queue_prev(&frame->queue);\n\n        if (prev != ngx_queue_sentinel(&ctx->frames)) {\n            ctx->last_priority = ngx_queue_data(prev, ngx_quic_frame_t, queue);\n        } else {\n            ctx->last_priority = NULL;\n        }\n    }\n\n    ngx_queue_remove(&frame->queue);\n}\n\n\nngx_int_t\nngx_quic_split_frame(ngx_connection_t *c, ngx_quic_frame_t *f, size_t len)\n{\n    size_t                     shrink;\n    ngx_quic_frame_t          *nf;\n    ngx_quic_ordered_frame_t  *of, *onf;\n    ngx_quic_send_ctx_t       *ctx;\n    ngx_quic_connection_t      *qc;\n\n    qc = ngx_quic_get_connection(c);\n    ctx = ngx_quic_get_send_ctx(qc, f->level);\n\n    switch (f->type) {\n    case NGX_QUIC_FT_CRYPTO:\n    case NGX_QUIC_FT_STREAM:\n        break;\n\n    default:\n        return NGX_DECLINED;\n    }\n\n    if ((size_t) f->len <= len) {\n        return NGX_OK;\n    }\n\n    shrink = f->len - len;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic split frame now:%uz need:%uz shrink:%uz\",\n                   f->len, len, shrink);\n\n    of = &f->u.ord;\n\n    if (of->length <= shrink) {\n        return NGX_DECLINED;\n    }\n\n    of->length -= shrink;\n    f->len = ngx_quic_create_frame(NULL, f);\n\n    if ((size_t) f->len > len) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"could not split QUIC frame\");\n        return NGX_ERROR;\n    }\n\n    nf = ngx_quic_alloc_frame(c);\n    if (nf == NULL) {\n        return NGX_ERROR;\n    }\n\n    *nf = *f;\n    onf = &nf->u.ord;\n    onf->offset += of->length;\n    onf->length = shrink;\n    nf->len = ngx_quic_create_frame(NULL, nf);\n\n    nf->data = ngx_quic_split_bufs(c, f->data, of->length);\n    if (nf->data == NGX_CHAIN_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ngx_queue_insert_after(&f->queue, &nf->queue);\n\n    if (ctx->last_priority == f) {\n        ctx->last_priority = nf;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_chain_t *\nngx_quic_split_bufs(ngx_connection_t *c, ngx_chain_t *in, size_t len)\n{\n    size_t                  n;\n    ngx_buf_t              *b;\n    ngx_chain_t            *out;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    while (in) {\n        n = ngx_buf_size(in->buf);\n\n        if (n == len) {\n            out = in->next;\n            in->next = NULL;\n            return out;\n        }\n\n        if (n > len) {\n            break;\n        }\n\n        len -= n;\n        in = in->next;\n    }\n\n    if (in == NULL) {\n        return NULL;\n    }\n\n    /* split in->buf by creating shadow bufs which reference it */\n\n    if (in->buf->shadow == NULL) {\n        if (qc->free_shadow_bufs) {\n            b = qc->free_shadow_bufs;\n            qc->free_shadow_bufs = b->shadow;\n\n        } else {\n            b = ngx_alloc_buf(c->pool);\n            if (b == NULL) {\n                return NGX_CHAIN_ERROR;\n            }\n        }\n\n        *b = *in->buf;\n        b->shadow = in->buf;\n        b->last_shadow = 1;\n        in->buf = b;\n    }\n\n    out = ngx_alloc_chain_link(c->pool);\n    if (out == NULL) {\n        return NGX_CHAIN_ERROR;\n    }\n\n    if (qc->free_shadow_bufs) {\n        b = qc->free_shadow_bufs;\n        qc->free_shadow_bufs = b->shadow;\n\n    } else {\n        b = ngx_alloc_buf(c->pool);\n        if (b == NULL) {\n            ngx_free_chain(c->pool, out);\n            return NGX_CHAIN_ERROR;\n        }\n    }\n\n    out->buf = b;\n    out->next = in->next;\n    in->next = NULL;\n\n    *b = *in->buf;\n    b->last_shadow = 0;\n    b->pos = b->pos + len;\n\n    in->buf->shadow = b;\n    in->buf->last = in->buf->pos + len;\n\n    return out;\n}\n\n\nngx_chain_t *\nngx_quic_alloc_buf(ngx_connection_t *c)\n{\n    ngx_buf_t              *b;\n    ngx_chain_t            *cl;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc->free_bufs) {\n        cl = qc->free_bufs;\n        qc->free_bufs = cl->next;\n\n        b = cl->buf;\n        b->pos = b->start;\n        b->last = b->start;\n\n#ifdef NGX_QUIC_DEBUG_ALLOC\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic reuse buffer n:%ui\", qc->nbufs);\n#endif\n\n        return cl;\n    }\n\n    cl = ngx_alloc_chain_link(c->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    b = ngx_create_temp_buf(c->pool, NGX_QUIC_BUFFER_SIZE);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->tag = (ngx_buf_tag_t) &ngx_quic_alloc_buf;\n\n    cl->buf = b;\n\n#ifdef NGX_QUIC_DEBUG_ALLOC\n    ++qc->nbufs;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic alloc buffer n:%ui\", qc->nbufs);\n#endif\n\n    return cl;\n}\n\n\nngx_chain_t *\nngx_quic_copy_buf(ngx_connection_t *c, u_char *data, size_t len)\n{\n    size_t        n;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl, *out, **ll;\n\n    out = NULL;\n    ll = &out;\n\n    while (len) {\n        cl = ngx_quic_alloc_buf(c);\n        if (cl == NULL) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        b = cl->buf;\n        n = ngx_min((size_t) (b->end - b->last), len);\n\n        b->last = ngx_cpymem(b->last, data, n);\n\n        data += n;\n        len -= n;\n\n        *ll = cl;\n        ll = &cl->next;\n    }\n\n    *ll = NULL;\n\n    return out;\n}\n\n\nngx_chain_t *\nngx_quic_copy_chain(ngx_connection_t *c, ngx_chain_t *in, size_t limit)\n{\n    size_t        n;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl, *out, **ll;\n\n    out = NULL;\n    ll = &out;\n\n    while (in) {\n        if (!ngx_buf_in_memory(in->buf) || ngx_buf_size(in->buf) == 0) {\n            in = in->next;\n            continue;\n        }\n\n        cl = ngx_quic_alloc_buf(c);\n        if (cl == NULL) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        *ll = cl;\n        ll = &cl->next;\n\n        b = cl->buf;\n\n        while (in && b->last != b->end) {\n\n            n = ngx_min(in->buf->last - in->buf->pos, b->end - b->last);\n\n            if (limit > 0 && n > limit) {\n                n = limit;\n            }\n\n            b->last = ngx_cpymem(b->last, in->buf->pos, n);\n\n            in->buf->pos += n;\n            if (in->buf->pos == in->buf->last) {\n                in = in->next;\n            }\n\n            if (limit > 0) {\n                if (limit == n) {\n                    goto done;\n                }\n\n                limit -= n;\n            }\n        }\n    }\n\ndone:\n\n    *ll = NULL;\n\n    return out;\n}\n\n\nngx_int_t\nngx_quic_order_bufs(ngx_connection_t *c, ngx_chain_t **out, ngx_chain_t *in,\n    size_t offset)\n{\n    u_char       *p;\n    size_t        n;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl, *sl;\n\n    while (in) {\n        cl = *out;\n\n        if (cl == NULL) {\n            cl = ngx_quic_alloc_buf(c);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl->buf->last = cl->buf->end;\n            cl->buf->sync = 1; /* hole */\n            cl->next = NULL;\n            *out = cl;\n        }\n\n        b = cl->buf;\n        n = b->last - b->pos;\n\n        if (n <= offset) {\n            offset -= n;\n            out = &cl->next;\n            continue;\n        }\n\n        if (b->sync && offset > 0) {\n            sl = ngx_quic_split_bufs(c, cl, offset);\n            if (sl == NGX_CHAIN_ERROR) {\n                return NGX_ERROR;\n            }\n\n            cl->next = sl;\n            continue;\n        }\n\n        for (p = b->pos + offset; p != b->last && in; /* void */ ) {\n            n = ngx_min(b->last - p, in->buf->last - in->buf->pos);\n\n            if (b->sync) {\n                ngx_memcpy(p, in->buf->pos, n);\n            }\n\n            p += n;\n            in->buf->pos += n;\n            offset += n;\n\n            if (in->buf->pos == in->buf->last) {\n                in = in->next;\n            }\n        }\n\n        if (b->sync && p != b->pos) {\n            sl = ngx_quic_split_bufs(c, cl, p - b->pos);\n            if (sl == NGX_CHAIN_ERROR) {\n                return NGX_ERROR;\n            }\n\n            cl->next = sl;\n            cl->buf->sync = 0;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\n#if (NGX_DEBUG)\n\nvoid\nngx_quic_log_frame(ngx_log_t *log, ngx_quic_frame_t *f, ngx_uint_t tx)\n{\n    u_char      *p, *last, *pos, *end;\n    ssize_t      n;\n    uint64_t     gap, range, largest, smallest;\n    ngx_uint_t   i;\n    u_char       buf[NGX_MAX_ERROR_STR];\n\n    p = buf;\n    last = buf + sizeof(buf);\n\n    switch (f->type) {\n\n    case NGX_QUIC_FT_CRYPTO:\n        p = ngx_slprintf(p, last, \"CRYPTO len:%uL off:%uL\",\n                         f->u.crypto.length, f->u.crypto.offset);\n\n#ifdef NGX_QUIC_DEBUG_FRAMES\n        {\n            ngx_chain_t  *cl;\n\n            p = ngx_slprintf(p, last, \" data:\");\n\n            for (cl = f->data; cl; cl = cl->next) {\n                p = ngx_slprintf(p, last, \"%*xs\",\n                                 cl->buf->last - cl->buf->pos, cl->buf->pos);\n            }\n        }\n#endif\n\n        break;\n\n    case NGX_QUIC_FT_PADDING:\n        p = ngx_slprintf(p, last, \"PADDING\");\n        break;\n\n    case NGX_QUIC_FT_ACK:\n    case NGX_QUIC_FT_ACK_ECN:\n\n        p = ngx_slprintf(p, last, \"ACK n:%ui delay:%uL \",\n                         f->u.ack.range_count, f->u.ack.delay);\n\n        if (f->data) {\n            pos = f->data->buf->pos;\n            end = f->data->buf->last;\n\n        } else {\n            pos = NULL;\n            end = NULL;\n        }\n\n        largest = f->u.ack.largest;\n        smallest = f->u.ack.largest - f->u.ack.first_range;\n\n        if (largest == smallest) {\n            p = ngx_slprintf(p, last, \"%uL\", largest);\n\n        } else {\n            p = ngx_slprintf(p, last, \"%uL-%uL\", largest, smallest);\n        }\n\n        for (i = 0; i < f->u.ack.range_count; i++) {\n            n = ngx_quic_parse_ack_range(log, pos, end, &gap, &range);\n            if (n == NGX_ERROR) {\n                break;\n            }\n\n            pos += n;\n\n            largest = smallest - gap - 2;\n            smallest = largest - range;\n\n            if (largest == smallest) {\n                p = ngx_slprintf(p, last, \" %uL\", largest);\n\n            } else {\n                p = ngx_slprintf(p, last, \" %uL-%uL\", largest, smallest);\n            }\n        }\n\n        if (f->type == NGX_QUIC_FT_ACK_ECN) {\n            p = ngx_slprintf(p, last, \" ECN counters ect0:%uL ect1:%uL ce:%uL\",\n                             f->u.ack.ect0, f->u.ack.ect1, f->u.ack.ce);\n        }\n        break;\n\n    case NGX_QUIC_FT_PING:\n        p = ngx_slprintf(p, last, \"PING\");\n        break;\n\n    case NGX_QUIC_FT_NEW_CONNECTION_ID:\n        p = ngx_slprintf(p, last,\n                         \"NEW_CONNECTION_ID seq:%uL retire:%uL len:%ud\",\n                         f->u.ncid.seqnum, f->u.ncid.retire, f->u.ncid.len);\n        break;\n\n    case NGX_QUIC_FT_RETIRE_CONNECTION_ID:\n        p = ngx_slprintf(p, last, \"RETIRE_CONNECTION_ID seqnum:%uL\",\n                         f->u.retire_cid.sequence_number);\n        break;\n\n    case NGX_QUIC_FT_CONNECTION_CLOSE:\n    case NGX_QUIC_FT_CONNECTION_CLOSE_APP:\n        p = ngx_slprintf(p, last, \"CONNECTION_CLOSE%s err:%ui\",\n                         f->type == NGX_QUIC_FT_CONNECTION_CLOSE ? \"\" : \"_APP\",\n                         f->u.close.error_code);\n\n        if (f->u.close.reason.len) {\n            p = ngx_slprintf(p, last, \" %V\", &f->u.close.reason);\n        }\n\n        if (f->type == NGX_QUIC_FT_CONNECTION_CLOSE) {\n            p = ngx_slprintf(p, last, \" ft:%ui\", f->u.close.frame_type);\n        }\n\n        break;\n\n    case NGX_QUIC_FT_STREAM:\n        p = ngx_slprintf(p, last, \"STREAM id:0x%xL\", f->u.stream.stream_id);\n\n        if (f->u.stream.off) {\n            p = ngx_slprintf(p, last, \" off:%uL\", f->u.stream.offset);\n        }\n\n        if (f->u.stream.len) {\n            p = ngx_slprintf(p, last, \" len:%uL\", f->u.stream.length);\n        }\n\n        if (f->u.stream.fin) {\n            p = ngx_slprintf(p, last, \" fin:1\");\n        }\n\n#ifdef NGX_QUIC_DEBUG_FRAMES\n        {\n            ngx_chain_t  *cl;\n\n            p = ngx_slprintf(p, last, \" data:\");\n\n            for (cl = f->data; cl; cl = cl->next) {\n                p = ngx_slprintf(p, last, \"%*xs\",\n                                 cl->buf->last - cl->buf->pos, cl->buf->pos);\n            }\n        }\n#endif\n\n        break;\n\n    case NGX_QUIC_FT_MAX_DATA:\n        p = ngx_slprintf(p, last, \"MAX_DATA max_data:%uL on recv\",\n                         f->u.max_data.max_data);\n        break;\n\n    case NGX_QUIC_FT_RESET_STREAM:\n        p = ngx_slprintf(p, last, \"RESET_STREAM\"\n                        \" id:0x%xL error_code:0x%xL final_size:0x%xL\",\n                        f->u.reset_stream.id, f->u.reset_stream.error_code,\n                        f->u.reset_stream.final_size);\n        break;\n\n    case NGX_QUIC_FT_STOP_SENDING:\n        p = ngx_slprintf(p, last, \"STOP_SENDING id:0x%xL err:0x%xL\",\n                         f->u.stop_sending.id, f->u.stop_sending.error_code);\n        break;\n\n    case NGX_QUIC_FT_STREAMS_BLOCKED:\n    case NGX_QUIC_FT_STREAMS_BLOCKED2:\n        p = ngx_slprintf(p, last, \"STREAMS_BLOCKED limit:%uL bidi:%ui\",\n                         f->u.streams_blocked.limit, f->u.streams_blocked.bidi);\n        break;\n\n    case NGX_QUIC_FT_MAX_STREAMS:\n    case NGX_QUIC_FT_MAX_STREAMS2:\n        p = ngx_slprintf(p, last, \"MAX_STREAMS limit:%uL bidi:%ui\",\n                         f->u.max_streams.limit, f->u.max_streams.bidi);\n        break;\n\n    case NGX_QUIC_FT_MAX_STREAM_DATA:\n        p = ngx_slprintf(p, last, \"MAX_STREAM_DATA id:0x%xL limit:%uL\",\n                         f->u.max_stream_data.id, f->u.max_stream_data.limit);\n        break;\n\n\n    case NGX_QUIC_FT_DATA_BLOCKED:\n        p = ngx_slprintf(p, last, \"DATA_BLOCKED limit:%uL\",\n                         f->u.data_blocked.limit);\n        break;\n\n    case NGX_QUIC_FT_STREAM_DATA_BLOCKED:\n        p = ngx_slprintf(p, last, \"STREAM_DATA_BLOCKED id:0x%xL limit:%uL\",\n                         f->u.stream_data_blocked.id,\n                         f->u.stream_data_blocked.limit);\n        break;\n\n    case NGX_QUIC_FT_PATH_CHALLENGE:\n        p = ngx_slprintf(p, last, \"PATH_CHALLENGE data:0x%*xs\",\n                         sizeof(f->u.path_challenge.data),\n                         f->u.path_challenge.data);\n        break;\n\n    case NGX_QUIC_FT_PATH_RESPONSE:\n        p = ngx_slprintf(p, last, \"PATH_RESPONSE data:0x%*xs\",\n                         sizeof(f->u.path_challenge.data),\n                         f->u.path_challenge.data);\n        break;\n\n    case NGX_QUIC_FT_NEW_TOKEN:\n        p = ngx_slprintf(p, last, \"NEW_TOKEN\");\n        break;\n\n    case NGX_QUIC_FT_HANDSHAKE_DONE:\n        p = ngx_slprintf(p, last, \"HANDSHAKE DONE\");\n        break;\n\n    default:\n        p = ngx_slprintf(p, last, \"unknown type 0x%xi\", f->type);\n        break;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0, \"quic frame %s %s %*s\",\n                   tx ? \"tx\" : \"rx\", ngx_quic_level_name(f->level),\n                   p - buf, buf);\n}\n\n#endif\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_frames.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_FRAMES_H_INCLUDED_\n#define _NGX_EVENT_QUIC_FRAMES_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c,\n    ngx_quic_frame_t *frame, void *data);\n\n\nngx_quic_frame_t *ngx_quic_alloc_frame(ngx_connection_t *c);\nvoid ngx_quic_free_frame(ngx_connection_t *c, ngx_quic_frame_t *frame);\nvoid ngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames);\nvoid ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame);\nvoid ngx_quic_queue_frame_priority(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame, ngx_int_t create);\nvoid ngx_quic_queue_frame_remove(ngx_quic_connection_t *qc, ngx_queue_t *queue, ngx_quic_frame_t *frame);\nvoid ngx_quic_queue_frame_after(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame, ngx_queue_t *queue, ngx_int_t create);\nngx_int_t ngx_quic_split_frame(ngx_connection_t *c, ngx_quic_frame_t *f,\n    size_t len);\n\nngx_chain_t *ngx_quic_alloc_buf(ngx_connection_t *c);\nngx_chain_t *ngx_quic_copy_buf(ngx_connection_t *c, u_char *data,\n    size_t len);\nngx_chain_t *ngx_quic_copy_chain(ngx_connection_t *c, ngx_chain_t *in,\n    size_t limit);\nvoid ngx_quic_trim_bufs(ngx_chain_t *in, size_t size);\nvoid ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in);\nngx_int_t ngx_quic_order_bufs(ngx_connection_t *c, ngx_chain_t **out,\n    ngx_chain_t *in, size_t offset);\n\n#if (NGX_DEBUG)\nvoid ngx_quic_log_frame(ngx_log_t *log, ngx_quic_frame_t *f, ngx_uint_t tx);\n#else\n#define ngx_quic_log_frame(log, f, tx)\n#endif\n\n#endif /* _NGX_EVENT_QUIC_FRAMES_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_migration.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#include <ngx_event_quic_connection.h>\n\n\nstatic void ngx_quic_set_connection_path(ngx_connection_t *c,\n    ngx_quic_path_t *path);\nstatic ngx_int_t ngx_quic_validate_path(ngx_connection_t *c,\n    ngx_quic_socket_t *qsock);\nstatic ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c,\n    ngx_quic_path_t *path);\nstatic ngx_int_t ngx_quic_path_restore(ngx_connection_t *c);\nstatic ngx_quic_path_t *ngx_quic_alloc_path(ngx_connection_t *c);\n\n\nngx_int_t\nngx_quic_send_path_cc(ngx_connection_t *c, struct sockaddr *sockaddr, socklen_t socklen)\n{\n    ssize_t                 sent;\n    ngx_quic_frame_t        frame;\n    ngx_quic_connection_t  *qc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic path send close connection\");\n\n    ngx_memzero(&frame, sizeof(ngx_quic_frame_t));\n\n    qc = ngx_quic_get_connection(c);\n\n    frame.level = qc->error_level;\n    frame.type = NGX_QUIC_FT_CONNECTION_CLOSE;\n    frame.u.close.error_code = NGX_QUIC_ERR_NO_ERROR;\n\n    frame.u.close.reason.len = sizeof(\"Migration disabled\") - 1;\n    frame.u.close.reason.data = (u_char *) \"Migration disabled\";\n\n    sent = ngx_quic_frame_sendto(c, &frame, 0, sockaddr, socklen);\n    if (sent == -1) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_quic_handle_path_challenge_frame(ngx_connection_t *c,\n    ngx_quic_path_challenge_frame_t *f)\n{\n    off_t                   max, pad;\n    ssize_t                 sent;\n    ngx_quic_path_t        *path;\n    ngx_quic_frame_t        frame, *fp;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    frame.level = ssl_encryption_application;\n    frame.type = NGX_QUIC_FT_PATH_RESPONSE;\n    frame.u.path_response = *f;\n\n    /*\n     * RFC 9000, 8.2.2.  Path Validation Responses\n     *\n     * A PATH_RESPONSE frame MUST be sent on the network path where the\n     * PATH_CHALLENGE frame was received.\n     */\n    qsock = ngx_quic_get_socket(c);\n    path = qsock->path;\n\n    /*\n     * An endpoint MUST expand datagrams that contain a PATH_RESPONSE frame\n     * to at least the smallest allowed maximum datagram size of 1200 bytes.\n     * ...\n     * An endpoint MUST NOT expand the datagram containing the PATH_RESPONSE\n     * if the resulting data exceeds the anti-amplification limit.\n     */\n    if (path->state != NGX_QUIC_PATH_VALIDATED) {\n        max = path->received * 3;\n        max = (path->sent >= max) ? 0 : max - path->sent;\n        pad = ngx_min(1200, max);\n\n    } else {\n        pad = 1200;\n    }\n\n    sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen);\n    if (sent < 0) {\n        return NGX_ERROR;\n    }\n\n    path->sent += sent;\n\n    if (qsock == qc->socket) {\n        /*\n         * RFC 9000, 9.3.3.  Off-Path Packet Forwarding\n         *\n         * An endpoint that receives a PATH_CHALLENGE on an active path SHOULD\n         * send a non-probing packet in response.\n         */\n\n        fp = ngx_quic_alloc_frame(c);\n        if (fp == NULL) {\n            return NGX_ERROR;\n        }\n\n        fp->level = ssl_encryption_application;\n        fp->type = NGX_QUIC_FT_PING;\n\n        ngx_quic_queue_frame(qc, fp);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_handle_path_response_frame(ngx_connection_t *c,\n    ngx_quic_path_challenge_frame_t *f)\n{\n    ngx_queue_t            *q;\n    ngx_quic_path_t        *path, *prev;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    /*\n     * RFC 9000, 8.2.3.  Successful Path Validation\n     *\n     * A PATH_RESPONSE frame received on any network path validates the path\n     * on which the PATH_CHALLENGE was sent.\n     */\n\n    for (q = ngx_queue_head(&qc->paths);\n         q != ngx_queue_sentinel(&qc->paths);\n         q = ngx_queue_next(q))\n    {\n        path = ngx_queue_data(q, ngx_quic_path_t, queue);\n\n        if (path->state != NGX_QUIC_PATH_VALIDATING) {\n            continue;\n        }\n\n        if (ngx_memcmp(path->challenge1, f->data, sizeof(f->data)) == 0\n            || ngx_memcmp(path->challenge2, f->data, sizeof(f->data)) == 0)\n        {\n            goto valid;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                  \"quic stale PATH_RESPONSE ignored\");\n\n    return NGX_OK;\n\nvalid:\n\n    /*\n     * RFC 9000, 9.4.  Loss Detection and Congestion Control\n     *\n     * On confirming a peer's ownership of its new address,\n     * an endpoint MUST immediately reset the congestion controller\n     * and round-trip time estimator for the new path to initial values\n     * unless the only change in the peer's address is its port number.\n     */\n\n    prev = qc->backup->path;\n\n    if (ngx_cmp_sockaddr(prev->sockaddr, prev->socklen,\n                         path->sockaddr, path->socklen, 0)\n        != NGX_OK)\n    {\n        /* address has changed */\n        ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));\n\n        if (qc->conf->initial_window) {\n            qc->congestion.window = qc->conf->initial_window;\n        } else {\n            qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,\n                                        ngx_max(2 * qc->tp.max_udp_payload_size,\n                                                14720));\n        }\n\n        qc->congestion.ssthresh = (size_t) -1;\n        qc->congestion.recovery_start = ngx_current_msec;\n    }\n\n    /*\n     * RFC 9000, 9.3.  Responding to Connection Migration\n     *\n     *  After verifying a new client address, the server SHOULD\n     *  send new address validation tokens (Section 8) to the client.\n     */\n\n    if (ngx_quic_send_new_token(c, path) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                   \"quic path #%uL successfully validated\", path->seqnum);\n\n    path->state = NGX_QUIC_PATH_VALIDATED;\n    path->validated_at = ngx_time();\n\n    return NGX_OK;\n}\n\n\nstatic ngx_quic_path_t *\nngx_quic_alloc_path(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    struct sockaddr        *sa;\n    ngx_quic_path_t        *path;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (!ngx_queue_empty(&qc->free_paths)) {\n\n        q = ngx_queue_head(&qc->free_paths);\n        path = ngx_queue_data(q, ngx_quic_path_t, queue);\n\n        ngx_queue_remove(&path->queue);\n\n        sa = path->sockaddr;\n        ngx_memzero(path, sizeof(ngx_quic_path_t));\n        path->sockaddr = sa;\n\n    } else {\n\n        path = ngx_pcalloc(c->pool, sizeof(ngx_quic_path_t));\n        if (path == NULL) {\n            return NULL;\n        }\n\n        path->sockaddr = ngx_palloc(c->pool, NGX_SOCKADDRLEN);\n        if (path->sockaddr == NULL) {\n            return NULL;\n        }\n    }\n\n    return path;\n}\n\n\nngx_quic_path_t *\nngx_quic_add_path(ngx_connection_t *c, struct sockaddr *sockaddr,\n    socklen_t socklen)\n{\n    ngx_quic_path_t        *path;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    path = ngx_quic_alloc_path(c);\n    if (path == NULL) {\n        return NULL;\n    }\n\n    path->seqnum = qc->path_seqnum++;\n\n    path->socklen = socklen;\n    ngx_memcpy(path->sockaddr, sockaddr, socklen);\n\n    path->addr_text.data = path->text;\n    path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text,\n                                        NGX_SOCKADDR_STRLEN, 1);\n\n    ngx_queue_insert_tail(&qc->paths, &path->queue);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic path #%uL created src:%V\",\n                   path->seqnum, &path->addr_text);\n\n    return path;\n}\n\n\nngx_quic_path_t *\nngx_quic_find_path(ngx_connection_t *c, struct sockaddr *sockaddr,\n    socklen_t socklen)\n{\n    ngx_queue_t            *q;\n    ngx_quic_path_t        *path;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    for (q = ngx_queue_head(&qc->paths);\n         q != ngx_queue_sentinel(&qc->paths);\n         q = ngx_queue_next(q))\n    {\n        path = ngx_queue_data(q, ngx_quic_path_t, queue);\n\n        if (ngx_cmp_sockaddr(sockaddr, socklen,\n                             path->sockaddr, path->socklen, 1)\n            == NGX_OK)\n        {\n            return path;\n        }\n    }\n\n    return NULL;\n}\n\n\nngx_int_t\nngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt)\n{\n    off_t                   len;\n    ngx_quic_path_t        *path;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_client_id_t   *cid;\n    ngx_quic_connection_t  *qc;\n\n    qsock = ngx_quic_get_socket(c);\n\n    if (c->udp->dgram == NULL) {\n        /* 1st ever packet in connection, path already exists */\n        path = qsock->path;\n        goto update;\n    }\n\n    path = ngx_quic_find_path(c, c->udp->dgram->sockaddr,\n                              c->udp->dgram->socklen);\n\n    if (path == NULL) {\n        path = ngx_quic_add_path(c, c->udp->dgram->sockaddr,\n                                 c->udp->dgram->socklen);\n        if (path == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (qsock->path) {\n            /* NAT rebinding case: packet to same CID, but from new address */\n\n            ngx_quic_unref_path(c, qsock->path);\n\n            qsock->path = path;\n            path->refcnt++;\n\n            goto update;\n        }\n\n    } else if (qsock->path) {\n        goto update;\n    }\n\n    /* prefer unused client IDs if available */\n    cid = ngx_quic_next_client_id(c);\n    if (cid == NULL) {\n\n        /* try to reuse connection ID used on the same path */\n        cid = ngx_quic_used_client_id(c, path);\n        if (cid == NULL) {\n\n            qc = ngx_quic_get_connection(c);\n            qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;\n            qc->error_reason = \"no available client ids for new path\";\n\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"no available client ids for new path\");\n\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_quic_connect(c, qsock, path, cid);\n\nupdate:\n\n    len = pkt->raw->last - pkt->raw->start;\n\n    /* TODO: this may be too late in some cases;\n     *       for example, if error happens during decrypt(), we cannot\n     *       send CC, if error happens in 1st packet, due to amplification\n     *       limit, because path->received = 0\n     *\n     *       should we account garbage as received or only decrypting packets?\n     */\n    path->received += len;\n\n    ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic packet via #%uL:%uL:%uL\"\n                   \" size:%O path recvd:%O sent:%O\",\n                   qsock->sid.seqnum, qsock->cid->seqnum, path->seqnum,\n                   len, path->received, path->sent);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_quic_set_connection_path(ngx_connection_t *c, ngx_quic_path_t *path)\n{\n    size_t  len;\n\n    ngx_memcpy(c->sockaddr, path->sockaddr,  path->socklen);\n    c->socklen = path->socklen;\n\n    if (c->addr_text.data) {\n        len = ngx_min(c->addr_text.len, path->addr_text.len);\n\n        ngx_memcpy(c->addr_text.data, path->addr_text.data, len);\n        c->addr_text.len = len;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic send path set to #%uL addr:%V\",\n                   path->seqnum, &path->addr_text);\n}\n\n\nngx_int_t\nngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)\n{\n    ngx_quic_path_t        *next;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    /* got non-probing packet via non-active socket with different path */\n\n    qc = ngx_quic_get_connection(c);\n\n    /* current socket, different from active */\n    qsock = ngx_quic_get_socket(c);\n\n    next = qsock->path; /* going to migrate to this path... */\n\n    if (next == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                   \"quic migration from #%uL:%uL:%uL (%s)\"\n                   \" to #%uL:%uL:%uL (%s)\",\n                   qc->socket->sid.seqnum, qc->socket->cid->seqnum,\n                   qc->socket->path->seqnum,\n                   ngx_quic_path_state_str(qc->socket->path),\n                   qsock->sid.seqnum, qsock->cid->seqnum, next->seqnum,\n                   ngx_quic_path_state_str(next));\n\n    switch (next->state) {\n    case NGX_QUIC_PATH_NEW:\n        if (ngx_quic_validate_path(c, qsock) != NGX_OK) {\n            return NGX_ERROR;\n        }\n        break;\n\n    /* migration to previously known path */\n\n    case NGX_QUIC_PATH_VALIDATING:\n        /* alredy validating, nothing to do */\n        break;\n\n    case NGX_QUIC_PATH_VALIDATED:\n        /* if path is old enough, revalidate */\n        if (ngx_time() - next->validated_at > NGX_QUIC_PATH_VALID_TIME) {\n\n            next->state = NGX_QUIC_PATH_NEW;\n\n            if (ngx_quic_validate_path(c, qsock) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        break;\n    }\n\n    ctx = ngx_quic_get_send_ctx(qc, pkt->level);\n\n    /*\n     * RFC 9000, 9.3.  Responding to Connection Migration\n     *\n     * An endpoint only changes the address to which it sends packets in\n     * response to the highest-numbered non-probing packet.\n     */\n    if (pkt->pn != ctx->largest_pn) {\n        return NGX_OK;\n    }\n\n    /* switching connection to new path */\n\n    ngx_quic_set_connection_path(c, next);\n\n    /*\n     * RFC 9000, 9.5.  Privacy Implications of Connection Migration\n     *\n     * An endpoint MUST NOT reuse a connection ID when sending to\n     * more than one destination address.\n     */\n\n    /* preserve valid path we are migrating from */\n    if (qc->socket->path->state == NGX_QUIC_PATH_VALIDATED) {\n\n        if (qc->backup) {\n            ngx_quic_close_socket(c, qc->backup);\n        }\n\n        qc->backup = qc->socket;\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                   \"quic backup socket is now #%uL:%uL:%uL (%s)\",\n                   qc->backup->sid.seqnum, qc->backup->cid->seqnum,\n                   qc->backup->path->seqnum,\n                   ngx_quic_path_state_str(qc->backup->path));\n    }\n\n    qc->socket = qsock;\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                   \"quic active socket is now #%uL:%uL:%uL (%s)\",\n                   qsock->sid.seqnum, qsock->cid->seqnum,\n                   qsock->path->seqnum, ngx_quic_path_state_str(qsock->path));\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_validate_path(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    ngx_msec_t              pto;\n    ngx_quic_path_t        *path;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    path = qsock->path;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic initiated validation of new path #%uL\",\n                   path->seqnum);\n\n    path->state = NGX_QUIC_PATH_VALIDATING;\n\n    if (RAND_bytes(path->challenge1, 8) != 1) {\n        return NGX_ERROR;\n    }\n\n    if (RAND_bytes(path->challenge2, 8) != 1) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_quic_send_path_challenge(c, path) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);\n    pto = ngx_quic_pto(c, ctx);\n\n    path->expires = ngx_current_msec + pto;\n    path->tries = NGX_QUIC_PATH_RETRIES;\n\n    if (!qc->path_validation.timer_set) {\n        ngx_add_timer(&qc->path_validation, pto);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)\n{\n    off_t             max, pad;\n    ssize_t           sent;\n    ngx_quic_frame_t  frame;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic path #%uL send path challenge tries:%ui\",\n                   path->seqnum, path->tries);\n\n    frame.level = ssl_encryption_application;\n    frame.type = NGX_QUIC_FT_PATH_CHALLENGE;\n\n    ngx_memcpy(frame.u.path_challenge.data, path->challenge1, 8);\n\n    /*\n     * RFC 9000, 8.2.1.  Initiating Path Validation\n     *\n     * An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame\n     * to at least the smallest allowed maximum datagram size of 1200 bytes,\n     * unless the anti-amplification limit for the path does not permit\n     * sending a datagram of this size.\n     */\n\n     /* same applies to PATH_RESPONSE frames */\n\n    max = path->received * 3;\n    max = (path->sent >= max) ? 0 : max - path->sent;\n    pad = ngx_min(NGX_QUIC_MIN_INITIAL_SIZE, max);\n\n    sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen);\n    if (sent < 0) {\n        return NGX_ERROR;\n    }\n\n    path->sent += sent;\n\n    ngx_memcpy(frame.u.path_challenge.data, path->challenge2, 8);\n\n    max = (path->sent >= max) ? 0 : max - path->sent;\n    pad = ngx_min(NGX_QUIC_MIN_INITIAL_SIZE, max);\n\n    sent = ngx_quic_frame_sendto(c, &frame, pad, path->sockaddr, path->socklen);\n    if (sent < 0) {\n        return NGX_ERROR;\n    }\n\n    path->sent += sent;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_path_validation_handler(ngx_event_t *ev)\n{\n    ngx_msec_t              now;\n    ngx_queue_t            *q;\n    ngx_msec_int_t          left, next, pto;\n    ngx_quic_path_t        *path;\n    ngx_connection_t       *c;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    c = ev->data;\n    qc = ngx_quic_get_connection(c);\n\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);\n    pto = ngx_quic_pto(c, ctx);\n\n    next = -1;\n    now = ngx_current_msec;\n\n    for (q = ngx_queue_head(&qc->paths);\n         q != ngx_queue_sentinel(&qc->paths);\n         q = ngx_queue_next(q))\n    {\n        path = ngx_queue_data(q, ngx_quic_path_t, queue);\n\n        if (path->state != NGX_QUIC_PATH_VALIDATING) {\n            continue;\n        }\n\n        left = path->expires - now;\n\n        if (left > 0) {\n\n            if (next == -1 || left < next) {\n                next = path->expires;\n            }\n\n            continue;\n        }\n\n        if (--path->tries) {\n            path->expires = ngx_current_msec + pto;\n\n            if (next == -1 || pto < next) {\n                next = pto;\n            }\n\n            /* retransmit */\n            (void) ngx_quic_send_path_challenge(c, path);\n\n            continue;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"quic path #%uL validation failed\", path->seqnum);\n\n        /* found expired path */\n\n        path->state = NGX_QUIC_PATH_NEW;\n\n        /*\n         * RFC 9000, 9.4.  Loss Detection and Congestion Control\n         *\n         * If the timer fires before the PATH_RESPONSE is received, the\n         * endpoint might send a new PATH_CHALLENGE and restart the timer for\n         * a longer period of time.  This timer SHOULD be set as described in\n         * Section 6.2.1 of [QUIC-RECOVERY] and MUST NOT be more aggressive.\n         */\n\n        if (qc->socket->path != path) {\n            /* the path was not actually used */\n            continue;\n        }\n\n        if (ngx_quic_path_restore(c) != NGX_OK) {\n            qc->error = NGX_QUIC_ERR_NO_VIABLE_PATH;\n            qc->error_reason = \"no viable path\";\n            ngx_quic_close_connection(c, NGX_ERROR);\n            return;\n        }\n    }\n\n    if (next != -1) {\n        ngx_add_timer(&qc->path_validation, next);\n    }\n}\n\n\nstatic ngx_int_t\nngx_quic_path_restore(ngx_connection_t *c)\n{\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    /*\n     * RFC 9000, 9.1.  Probing a New Path\n     *\n     * Failure to validate a path does not cause the connection to end\n     *\n     * RFC 9000, 9.3.2.  On-Path Address Spoofing\n     *\n     * To protect the connection from failing due to such a spurious\n     * migration, an endpoint MUST revert to using the last validated\n     * peer address when validation of a new peer address fails.\n     */\n\n    if (qc->backup == NULL) {\n        return NGX_ERROR;\n    }\n\n    qc->socket = qc->backup;\n    qc->backup = NULL;\n\n    qsock = qc->socket;\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                   \"quic active socket is restored to #%uL:%uL:%uL\"\n                   \" (%s), no backup\",\n                   qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum,\n                   ngx_quic_path_state_str(qsock->path));\n\n    ngx_quic_set_connection_path(c, qsock->path);\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_migration.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_MIGRATION_H_INCLUDED_\n#define _NGX_EVENT_QUIC_MIGRATION_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#define NGX_QUIC_PATH_RETRIES          3\n\n#define NGX_QUIC_PATH_NEW              0\n#define NGX_QUIC_PATH_VALIDATING       1\n#define NGX_QUIC_PATH_VALIDATED        2\n\n#define NGX_QUIC_PATH_VALID_TIME       600 /* seconds */\n\n\n#define ngx_quic_path_state_str(p)                                            \\\n    ((p)->state == NGX_QUIC_PATH_NEW) ? \"new\" :                               \\\n        (((p)->state == NGX_QUIC_PATH_VALIDATED) ? \"validated\" : \"validating\")\n\n\nngx_int_t ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,\n    ngx_quic_path_challenge_frame_t *f);\nngx_int_t ngx_quic_handle_path_response_frame(ngx_connection_t *c,\n    ngx_quic_path_challenge_frame_t *f);\n\nngx_quic_path_t *ngx_quic_find_path(ngx_connection_t *c,\n    struct sockaddr *sockaddr, socklen_t socklen);\nngx_quic_path_t *ngx_quic_add_path(ngx_connection_t *c,\n    struct sockaddr *sockaddr, socklen_t socklen);\n\nngx_int_t ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt);\nngx_int_t ngx_quic_handle_migration(ngx_connection_t *c,\n    ngx_quic_header_t *pkt);\n\nvoid ngx_quic_path_validation_handler(ngx_event_t *ev);\n\nngx_int_t ngx_quic_send_path_cc(ngx_connection_t *c, struct sockaddr *sockaddr, socklen_t socklen);\n\n#endif /* _NGX_EVENT_QUIC_MIGRATION_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_mtu.c",
    "content": "\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_quic_connection.h>\n\n\n// @see https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/quic_mtu_discovery.cc\n\nstatic ngx_int_t\nngx_quic_mtu_should_probe(ngx_quic_mtu_t *mtu, uint64_t largest_sent_packet)\n{\n    if (mtu->process) {\n        return 0;\n    }\n\n    if (mtu->min_probe_length >= mtu->max_probe_length) {\n        return 0;\n    }\n\n    if (mtu->remaining_probe_count == 0) {\n        return 0;\n    }\n\n    if (largest_sent_packet < mtu->next_probe_at) {\n        return 0;\n    }\n\n    return 1;\n}\n\n\nstatic size_t\nngx_quic_mtu_next_probe_packet_length(ngx_quic_mtu_t *mtu)\n{\n    size_t  normal_next_probe_length = (mtu->min_probe_length + mtu->max_probe_length + 1) / 2;\n\n    if (mtu->remaining_probe_count == 1 &&\n        normal_next_probe_length > mtu->last_probe_length)\n    {\n        /* If the previous probe succeeded, and there is only one last probe to\n         * end, use |max_probe_length_| for the last probe.\n         */\n        return mtu->max_probe_length;\n    }\n\n    return normal_next_probe_length;\n}\n\n\nstatic size_t\nngx_quic_mtu_get_updated_probe_size(ngx_quic_mtu_t *mtu, uint64_t largest_sent_packet)\n{\n    size_t  probe_packet_length = ngx_quic_mtu_next_probe_packet_length(mtu);\n\n    if (probe_packet_length == mtu->last_probe_length) {\n        /* The next probe packet is as big as the previous one. Assuming the\n         * previous one exceeded MTU, we need to decrease the probe packet length.\n         */\n        mtu->max_probe_length = probe_packet_length;\n    }\n\n    mtu->last_probe_length = ngx_quic_mtu_next_probe_packet_length(mtu);\n    mtu->next_probe_at = largest_sent_packet + mtu->packets_between_probes + 1;\n\n    if (mtu->remaining_probe_count > 0) {\n        --mtu->remaining_probe_count;\n    }\n\n    return mtu->last_probe_length;\n}\n\n\nngx_int_t\nngx_quic_mtu_probe(ngx_connection_t *c)\n{\n    ngx_quic_connection_t  *qc;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_path_t        *path;\n    ngx_quic_frame_t       *frame;\n    size_t                  len;\n\n    qc = ngx_quic_get_connection(c);\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);\n\n    if (!ngx_quic_mtu_should_probe(&qc->mtu, ctx->pnum)) {\n        return NGX_DECLINED;\n    }\n\n    path = ngx_quic_get_socket(c)->path;\n\n    if (path == NULL) {\n        return NGX_DECLINED;\n    }\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_OK;\n    }\n\n    frame->level = ctx->level;\n    frame->type = NGX_QUIC_FT_PING;\n    frame->flush = 1;\n    frame->need_ack = 1;\n    frame->probe = 1;\n\n    len = ngx_quic_mtu_get_updated_probe_size(&qc->mtu, ctx->pnum);\n\n    if (ngx_quic_frame_sendto_dont_fragment(c, frame, len, path->sockaddr,  path->socklen) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    qc->mtu.process = 1;\n    ngx_queue_insert_tail(&ctx->sent, &frame->queue);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n        \"quic mtu discover sent new packet: %z\", len);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_mtu_ack(ngx_connection_t *c, ngx_quic_frame_t *frame)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    qc->mtu.min_probe_length = frame->plen;\n    qc->ctp.max_udp_payload_size = frame->plen;\n\n    qc->mtu.process = 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n        \"quic mtu discover ack packet: %z\", qc->ctp.max_udp_payload_size);\n}\n\n\nvoid\nngx_quic_mtu_lost(ngx_connection_t *c, ngx_quic_frame_t *frame)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    qc->mtu.process = 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n        \"quic mtu discover lost packet: %z\", qc->mtu.last_probe_length);\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_mtu.h",
    "content": "\n#ifndef _NGX_EVENT_QUIC_MTU_H_INCLUDED_\n#define _NGX_EVENT_QUIC_MTU_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t\nngx_quic_mtu_probe(ngx_connection_t *c);\n\nvoid\nngx_quic_mtu_ack(ngx_connection_t *c, ngx_quic_frame_t *frame);\n\nvoid\nngx_quic_mtu_lost(ngx_connection_t *c, ngx_quic_frame_t *frame);\n\n\n#endif /* _NGX_EVENT_QUIC_MTU_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_output.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#include <ngx_event_quic_connection.h>\n\n\n#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT   1252\n#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT6  1232\n\n#define NGX_QUIC_MAX_UDP_SEGMENT_BUF  65487 /* 65K - IPv6 header */\n#define NGX_QUIC_MAX_SEGMENTS            64 /* UDP_MAX_SEGMENTS */\n\n#define NGX_QUIC_RETRY_TOKEN_LIFETIME     3 /* seconds */\n#define NGX_QUIC_NEW_TOKEN_LIFETIME     600 /* seconds */\n#define NGX_QUIC_RETRY_BUFFER_SIZE      256\n    /* 1 flags + 4 version + 3 x (1 + 20) s/o/dcid + itag + token(64) */\n\n/*\n * RFC 9000, 10.3.  Stateless Reset\n *\n * Endpoints MUST discard packets that are too small to be valid QUIC\n * packets.  With the set of AEAD functions defined in [QUIC-TLS],\n * short header packets that are smaller than 21 bytes are never valid.\n */\n#define NGX_QUIC_MIN_PKT_LEN             21\n\n#define NGX_QUIC_MIN_SR_PACKET           43 /* 5 rand + 16 srt + 22 padding */\n#define NGX_QUIC_MAX_SR_PACKET         NGX_QUIC_MIN_INITIAL_SIZE\n\n#define NGX_QUIC_CC_MIN_INTERVAL       1000 /* 1s */\n\n#define NGX_QUIC_SOCKET_RETRY_DELAY      10 /* ms, for NGX_AGAIN on write */\n\n#if (NGX_HAVE_UDP_SENDMMSG)\n#define NGX_QUIC_MAX_SENDMMSG            64\n#endif\n\nstatic ngx_int_t ngx_quic_socket_output(ngx_connection_t *c,\n    ngx_quic_socket_t *qsock);\nstatic ngx_int_t ngx_quic_create_datagrams(ngx_connection_t *c,\n    ngx_quic_socket_t *qsock);\nstatic void ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);\nstatic void ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    uint64_t pnum, ngx_quic_frame_t *last_priority);\n#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))\nstatic ngx_uint_t ngx_quic_allow_segmentation(ngx_connection_t *c,\n    ngx_quic_socket_t *qsock);\nstatic ngx_int_t ngx_quic_create_segments(ngx_connection_t *c,\n    ngx_quic_socket_t *qsock);\nstatic ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,\n    size_t len, struct sockaddr *sockaddr, socklen_t socklen, size_t segment);\n#endif\n#if (NGX_HAVE_UDP_SENDMMSG)\nstatic ngx_uint_t ngx_quic_allow_sendmmsg(ngx_connection_t *c, ngx_quic_socket_t *qsock);\nstatic ngx_int_t ngx_quic_create_sendmmsg(ngx_connection_t *c, ngx_quic_socket_t *qsock);\nstatic ssize_t ngx_quic_sendmmsg(ngx_connection_t *c, struct iovec *iov,\n    struct sockaddr *sockaddr, socklen_t socklen, ngx_int_t nseg);\n#endif\nstatic ssize_t ngx_quic_output_packet(ngx_connection_t *c,\n    ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min,\n    ngx_quic_socket_t *qsock);\nstatic void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    ngx_quic_socket_t *qsock, ngx_quic_header_t *pkt);\nstatic ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);\nstatic ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len,\n    struct sockaddr *sockaddr, socklen_t socklen);\nstatic void ngx_quic_set_packet_number(ngx_quic_header_t *pkt,\n    ngx_quic_send_ctx_t *ctx);\n\n\nsize_t\nngx_quic_max_udp_payload(ngx_connection_t *c)\n{\n#if (NGX_HAVE_INET6)\n    if (c->sockaddr->sa_family == AF_INET6) {\n        return NGX_QUIC_MAX_UDP_PAYLOAD_OUT6;\n    }\n#endif\n\n    return NGX_QUIC_MAX_UDP_PAYLOAD_OUT;\n}\n\n\nngx_int_t\nngx_quic_output(ngx_connection_t *c)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (ngx_quic_socket_output(c, qc->socket) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    if (qc->conf->mtu) {\n        ngx_quic_mtu_probe(c);\n    }\n#endif\n\n    ngx_quic_set_lost_timer(c);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_socket_output(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    size_t                  in_flight;\n    ngx_int_t               rc;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n\n    c->log->action = \"sending frames\";\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n\n    in_flight = cg->in_flight;\n\n#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))\n    if (ngx_quic_allow_segmentation(c, qsock)) {\n        rc = ngx_quic_create_segments(c, qsock);\n    } else\n#endif\n#if (NGX_HAVE_UDP_SENDMMSG)\n    if (ngx_quic_allow_sendmmsg(c, qsock)) {\n        rc = ngx_quic_create_sendmmsg(c, qsock);\n    } else\n#endif\n    {\n        rc = ngx_quic_create_datagrams(c, qsock);\n    }\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (in_flight != cg->in_flight && !qc->send_timer_set && !qc->closing) {\n        qc->send_timer_set = 1;\n        ngx_add_timer(c->read, qc->tp.max_idle_timeout);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_create_datagrams(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    off_t                   max;\n    size_t                  len, min;\n    ssize_t                 n;\n    u_char                 *p;\n    uint64_t                preserved_pnum[NGX_QUIC_SEND_CTX_LAST];\n    ngx_quic_frame_t       *preserved_last_priority[NGX_QUIC_SEND_CTX_LAST];\n    ngx_uint_t              i, pad;\n    ngx_quic_path_t        *path;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n    static u_char           dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n    path = qsock->path;\n\n    for ( ; ; ) {\n\n        p = dst;\n\n        len = ngx_min(qc->ctp.max_udp_payload_size,\n                      NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);\n\n        if (path->state != NGX_QUIC_PATH_VALIDATED) {\n            max = path->received * 3;\n            max = (path->sent >= max) ? 0 : max - path->sent;\n\n            len = ngx_min(len, (size_t) max);\n        }\n\n        pad = ngx_quic_get_padding_level(c);\n\n        for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n\n            ctx = &qc->send_ctx[i];\n\n            if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            preserved_pnum[i] = ctx->pnum;\n            preserved_last_priority[i] = ctx->last_priority;\n\n            if (cg->in_flight >= cg->window && ctx->last_priority == NULL) {\n                continue;\n            }\n\n            min = (i == pad && p - dst < NGX_QUIC_MIN_INITIAL_SIZE)\n                  ? NGX_QUIC_MIN_INITIAL_SIZE - (p - dst) : 0;\n\n            if (min > len) {\n                continue;\n            }\n\n            n = ngx_quic_output_packet(c, ctx, p, len, min, qsock);\n            if (n == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            p += n;\n            len -= n;\n        }\n\n        len = p - dst;\n        if (len == 0) {\n            break;\n        }\n\n        n = ngx_quic_send(c, dst, len, path->sockaddr, path->socklen);\n\n        if (n == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        if (n == NGX_AGAIN) {\n            for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n                ngx_quic_revert_send(c, &qc->send_ctx[i], preserved_pnum[i], preserved_last_priority[i]);\n            }\n\n            ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);\n            break;\n        }\n\n        for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n            ngx_quic_commit_send(c, &qc->send_ctx[i]);\n        }\n\n        path->sent += len;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)\n{\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *f;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    cg = &qc->congestion;\n\n    while (!ngx_queue_empty(&ctx->sending)) {\n        q = ngx_queue_head(&ctx->sending);\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        ngx_queue_remove(q);\n\n        if (f->pkt_need_ack && !qc->closing) {\n            ngx_queue_insert_tail(&ctx->sent, q);\n\n            cg->in_flight += f->plen;\n\n        } else {\n            ngx_quic_free_frame(c, f);\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic congestion send if:%uz\", cg->in_flight);\n}\n\n\nstatic void\nngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    uint64_t pnum, ngx_quic_frame_t *last_priority)\n{\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *f;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    while (!ngx_queue_empty(&ctx->sending)) {\n\n        q = ngx_queue_last(&ctx->sending);\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        ngx_queue_remove(q);\n        ngx_quic_queue_frame_after(qc, f, &ctx->frames, 0);\n    }\n\n    ctx->pnum = pnum;\n    ctx->last_priority = last_priority;\n}\n\n\n#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))\n\nstatic ngx_uint_t\nngx_quic_allow_segmentation(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    size_t                  bytes, len;\n    ngx_queue_t            *q, *sq;\n    ngx_quic_frame_t       *f;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n    ngx_quic_fqueue_t      *fqueue;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (!qc->conf->gso_enabled) {\n        return 0;\n    }\n\n    if (qsock->path->state != NGX_QUIC_PATH_VALIDATED) {\n        /* don't even try to be faster on non-validated paths */\n        return 0;\n    }\n\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);\n    if (!ngx_queue_empty(&ctx->frames)) {\n        return 0;\n    }\n\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);\n    if (!ngx_queue_empty(&ctx->frames)) {\n        return 0;\n    }\n\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);\n\n    bytes = 0;\n\n    len = ngx_min(qc->ctp.max_udp_payload_size,\n                  NGX_QUIC_MAX_UDP_SEGMENT_BUF);\n\n    for (sq = ngx_queue_head(&ctx->fqueues);\n         sq != ngx_queue_sentinel(&ctx->fqueues);\n         sq = ngx_queue_next(sq))\n    {\n        fqueue = ngx_queue_data(sq, ngx_quic_fqueue_t, queue);\n\n        for (q = ngx_queue_head(fqueue->frames);\n             q != ngx_queue_sentinel(fqueue->frames);\n             q = ngx_queue_next(q))\n        {\n            f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n            bytes += f->len;\n\n            if (bytes > len * 3) {\n                /* require at least ~3 full packets to batch */\n                return 1;\n            }\n        }\n    }\n\n    return 0;\n}\n\n\nstatic ngx_int_t\nngx_quic_create_segments(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    size_t                  len, segsize;\n    ssize_t                 n;\n    u_char                 *p, *end;\n    uint64_t                preserved_pnum;\n    ngx_quic_frame_t       *preserved_last_priority;\n    ngx_uint_t              nseg;\n    ngx_quic_path_t        *path;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n    static u_char           dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF];\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n    path = qsock->path;\n\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);\n\n    if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    segsize = ngx_min(qc->ctp.max_udp_payload_size,\n                      NGX_QUIC_MAX_UDP_SEGMENT_BUF);\n    p = dst;\n    end = dst + sizeof(dst);\n\n    nseg = 0;\n\n    preserved_pnum = ctx->pnum;\n    preserved_last_priority = ctx->last_priority;\n\n    for ( ;; ) {\n\n        len = ngx_min(segsize, (size_t) (end - p));\n\n        if (len && (cg->in_flight < cg->window || ctx->last_priority != NULL)) {\n\n            n = ngx_quic_output_packet(c, ctx, p, len, len, qsock);\n            if (n == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (n) {\n                p += n;\n                nseg++;\n            }\n\n        } else {\n            n = 0;\n        }\n\n        if (p == dst) {\n            break;\n        }\n\n        if (n == 0 || nseg == NGX_QUIC_MAX_SEGMENTS) {\n            n = ngx_quic_send_segments(c, dst, p - dst, path->sockaddr,\n                                       path->socklen, segsize);\n            if (n == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (n == NGX_AGAIN) {\n                ngx_quic_revert_send(c, ctx, preserved_pnum, preserved_last_priority);\n\n                ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);\n                break;\n            }\n\n            ngx_quic_commit_send(c, ctx);\n\n            path->sent += n;\n\n            p = dst;\n            nseg = 0;\n            preserved_pnum = ctx->pnum;\n            preserved_last_priority = ctx->last_priority;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ssize_t\nngx_quic_send_segments(ngx_connection_t *c, u_char *buf, size_t len,\n    struct sockaddr *sockaddr, socklen_t socklen, size_t segment)\n{\n    size_t           clen;\n    ssize_t          n;\n    uint16_t        *valp;\n    struct iovec     iov;\n    struct msghdr    msg;\n    struct cmsghdr  *cmsg;\n\n#if defined(NGX_HAVE_ADDRINFO_CMSG)\n    char             msg_control[CMSG_SPACE(sizeof(uint16_t))\n                             + CMSG_SPACE(sizeof(ngx_addrinfo_t))];\n#else\n    char             msg_control[CMSG_SPACE(sizeof(uint16_t))];\n#endif\n\n    ngx_memzero(&msg, sizeof(struct msghdr));\n    ngx_memzero(msg_control, sizeof(msg_control));\n\n    iov.iov_len = len;\n    iov.iov_base = buf;\n\n    msg.msg_iov = &iov;\n    msg.msg_iovlen = 1;\n\n    msg.msg_name = sockaddr;\n    msg.msg_namelen = socklen;\n\n    msg.msg_control = msg_control;\n    msg.msg_controllen = sizeof(msg_control);\n\n    cmsg = CMSG_FIRSTHDR(&msg);\n\n    cmsg->cmsg_level = SOL_UDP;\n    cmsg->cmsg_type = UDP_SEGMENT;\n    cmsg->cmsg_len = CMSG_LEN(sizeof(uint16_t));\n\n    clen = CMSG_SPACE(sizeof(uint16_t));\n\n    valp = (void *) CMSG_DATA(cmsg);\n    *valp = segment;\n\n#if defined(NGX_HAVE_ADDRINFO_CMSG)\n    if (c->listening && c->listening->wildcard && c->local_sockaddr) {\n        cmsg = CMSG_NXTHDR(&msg, cmsg);\n        clen += ngx_set_srcaddr_cmsg(cmsg, c->local_sockaddr);\n    }\n#endif\n\n    msg.msg_controllen = clen;\n\n    n = ngx_sendmsg(c, &msg, 0);\n    if (n < 0) {\n        return n;\n    }\n\n    c->sent += n;\n\n    return n;\n}\n\n#endif\n\n\n#if (NGX_HAVE_UDP_SENDMMSG)\n\nstatic ngx_uint_t\nngx_quic_allow_sendmmsg(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (!qc->conf->sendmmsg_enabled) {\n        return 0;\n    }\n\n    return 1;\n}\n\n\nstatic ngx_int_t\nngx_quic_create_sendmmsg(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    off_t                   max;\n    size_t                  len, min;\n    ssize_t                 n;\n    u_char                 *p, *dst;\n    ngx_uint_t              i, nseg, pad;\n    ngx_quic_path_t        *path;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_congestion_t  *cg;\n    ngx_quic_connection_t  *qc;\n    uint64_t                preserved_pnum[NGX_QUIC_SEND_CTX_LAST];\n    ngx_quic_frame_t       *preserved_last_priority[NGX_QUIC_SEND_CTX_LAST];\n    struct iovec            iov[NGX_QUIC_MAX_SENDMMSG];\n    static u_char           bufs[NGX_QUIC_MAX_SENDMMSG][NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n\n    qc = ngx_quic_get_connection(c);\n    cg = &qc->congestion;\n    path = qsock->path;\n    nseg = 0;\n\n    for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n        ctx = &qc->send_ctx[i];\n\n        if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        preserved_pnum[i] = ctx->pnum;\n        preserved_last_priority[i] = ctx->last_priority;\n    }\n\n    for ( ; ; ) {\n\n        dst = bufs[nseg];\n        p = dst;\n\n        len = ngx_min(qc->ctp.max_udp_payload_size,\n                      NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);\n\n        if (path->state != NGX_QUIC_PATH_VALIDATED) {\n            max = path->received * 3;\n            max = (path->sent >= max) ? 0 : max - path->sent;\n\n            len = ngx_min(len, (size_t) max);\n        }\n\n        pad = ngx_quic_get_padding_level(c);\n\n        for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n\n            ctx = &qc->send_ctx[i];\n\n            preserved_pnum[i] = ctx->pnum;\n            preserved_last_priority[i] = ctx->last_priority;\n\n            if (cg->in_flight >= cg->window && ctx->last_priority == NULL) {\n                continue;\n            }\n\n            min = (i == pad && p - dst < NGX_QUIC_MIN_INITIAL_SIZE)\n                  ? NGX_QUIC_MIN_INITIAL_SIZE - (p - dst) : 0;\n\n            if (min > len) {\n                continue;\n            }\n\n            n = ngx_quic_output_packet(c, ctx, p, len, min, qsock);\n            if (n == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            p += n;\n            len -= n;\n        }\n\n        len = p - dst;\n\n        if (len) {\n            iov[nseg].iov_base = dst;\n            iov[nseg].iov_len = len;\n\n            nseg++;\n        }\n\n        if (nseg == 0) {\n            break;\n        }\n\n        if (len == 0 || nseg == NGX_QUIC_MAX_SENDMMSG) {\n            n = ngx_quic_sendmmsg(c, iov, path->sockaddr,\n                                       path->socklen, nseg);\n\n            if (n == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            /* TODO: partial commit if n != nseg */\n\n            if (n == NGX_AGAIN) {\n                for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n                    ngx_quic_revert_send(c, &qc->send_ctx[i], preserved_pnum[i], preserved_last_priority[i]);\n                }\n\n                ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);\n                break;\n            }\n\n            for (i = 0; i < (ngx_uint_t) n; i++) {\n                path->sent += iov[i].iov_len;\n            }\n\n            for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {\n                ngx_quic_commit_send(c, &qc->send_ctx[i]);\n\n                preserved_pnum[i] = ctx->pnum;\n                preserved_last_priority[i] = ctx->last_priority;\n            }\n\n            nseg = 0;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ssize_t\nngx_quic_sendmmsg(ngx_connection_t *c, struct iovec *iov,\n    struct sockaddr *sockaddr, socklen_t socklen, ngx_int_t nseg)\n{\n    struct mmsghdr      msgs[NGX_QUIC_MAX_SENDMMSG];\n    ngx_int_t           n, i;\n\n#if defined(NGX_HAVE_ADDRINFO_CMSG)\n    struct cmsghdr     *cmsg;\n    char                msg_control[NGX_QUIC_MAX_SENDMMSG][CMSG_SPACE(sizeof(ngx_addrinfo_t))];\n#endif\n\n    ngx_memzero(msgs, sizeof(msgs));\n\n#if defined(NGX_HAVE_ADDRINFO_CMSG)\n    ngx_memzero(msg_control, sizeof(msg_control));\n#endif\n\n    for (i = 0; i < nseg; i++) {\n        msgs[i].msg_hdr.msg_iov = &iov[i];\n        msgs[i].msg_hdr.msg_iovlen = 1;\n\n        msgs[i].msg_hdr.msg_name = sockaddr;\n        msgs[i].msg_hdr.msg_namelen = socklen;\n\n#if defined(NGX_HAVE_ADDRINFO_CMSG)\n        if (c->listening && c->listening->wildcard && c->local_sockaddr) {\n\n            msgs[i].msg_hdr.msg_control = msg_control[i];\n            msgs[i].msg_hdr.msg_controllen = sizeof(msg_control[i]);\n\n            cmsg = CMSG_FIRSTHDR(&msgs[i].msg_hdr);\n\n            msgs[i].msg_hdr.msg_controllen = ngx_set_srcaddr_cmsg(cmsg, c->local_sockaddr);\n        }\n#endif\n    }\n\n    n = ngx_sendmmsg(c, msgs, nseg, 0);\n    if (n < 0) {\n        return n;\n    }\n\n    for (i = 0; i < n; i++) {\n        c->sent += iov[i].iov_len;\n    }\n\n    return n;\n}\n\n#endif\n\n\nstatic ngx_uint_t\nngx_quic_get_padding_level(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *f;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    /*\n     * RFC 9000, 14.1.  Initial Datagram Size\n     *\n     * Similarly, a server MUST expand the payload of all UDP datagrams\n     * carrying ack-eliciting Initial packets to at least the smallest\n     * allowed maximum datagram size of 1200 bytes.\n     */\n\n    qc = ngx_quic_get_connection(c);\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);\n\n    for (q = ngx_queue_head(&ctx->frames);\n         q != ngx_queue_sentinel(&ctx->frames);\n         q = ngx_queue_next(q))\n    {\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        if (f->need_ack) {\n            ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);\n\n            if (ngx_queue_empty(&ctx->frames)) {\n                return 0;\n            }\n\n            return 1;\n        }\n    }\n\n    return NGX_QUIC_SEND_CTX_LAST;\n}\n\n\nstatic ssize_t\nngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    u_char *data, size_t max, size_t min, ngx_quic_socket_t *qsock)\n{\n    size_t                  len, pad, min_payload, max_payload;\n    u_char                 *p;\n    ssize_t                 flen;\n    ngx_str_t               res;\n    ngx_int_t               rc;\n    ngx_uint_t              nframes, expand;\n    ngx_msec_t              now;\n    ngx_queue_t            *q;\n    ngx_quic_frame_t       *f, *first;\n    ngx_quic_header_t       pkt;\n    ngx_quic_connection_t  *qc;\n    static u_char           src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n    ngx_quic_fqueue_t      *fqueue;\n\n    if (ngx_queue_empty(&ctx->fqueues)) {\n        return 0;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic output sock #%uL %s packet max:%uz min:%uz\",\n                   qsock->sid.seqnum, ngx_quic_level_name(ctx->level),\n                   max, min);\n\n    qc = ngx_quic_get_connection(c);\n\n    ngx_quic_init_packet(c, ctx, qsock, &pkt);\n\n    min_payload = ngx_quic_payload_size(&pkt, min);\n    max_payload = ngx_quic_payload_size(&pkt, max);\n\n    /* RFC 9001, 5.4.2.  Header Protection Sample */\n    pad = 4 - pkt.num_len;\n    min_payload = ngx_max(min_payload, pad);\n\n    if (min_payload > max_payload) {\n        return 0;\n    }\n\n    now = ngx_current_msec;\n    nframes = 0;\n    p = src;\n    len = 0;\n    first = NULL;\n    expand = 0;\n\n    q = ngx_queue_head(&ctx->fqueues);\n    fqueue = ngx_queue_data(q, ngx_quic_fqueue_t, queue);\n\n    for (q = ngx_queue_head(fqueue->frames); ; )\n    {\n        if (q == ngx_queue_sentinel(fqueue->frames)) {\n            if (ngx_queue_empty(&ctx->fqueues)) {\n                break;\n            }\n\n            q = ngx_queue_head(&ctx->fqueues);\n            fqueue = ngx_queue_data(q, ngx_quic_fqueue_t, queue);\n\n            q = ngx_queue_head(fqueue->frames);\n        }\n\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n\n        if (!expand && (f->type == NGX_QUIC_FT_PATH_RESPONSE\n                        || f->type == NGX_QUIC_FT_PATH_CHALLENGE))\n        {\n            /*\n             * RFC 9000, 8.2.1.  Initiating Path Validation\n             *\n             * An endpoint MUST expand datagrams that contain a\n             * PATH_CHALLENGE frame to at least the smallest allowed\n             * maximum datagram size of 1200 bytes...\n             *\n             * (same applies to PATH_RESPONSE frames)\n             */\n\n            if (max < 1200) {\n                /* expanded packet will not fit */\n                break;\n            }\n\n            if (min < 1200) {\n                min = 1200;\n\n                min_payload = ngx_quic_payload_size(&pkt, min);\n            }\n\n            expand = 1;\n        }\n\n        if (len >= max_payload) {\n            break;\n        }\n\n        if (len + f->len > max_payload) {\n            rc = ngx_quic_split_frame(c, f, max_payload - len);\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_DECLINED) {\n                break;\n            }\n        }\n\n        if (f->need_ack) {\n            pkt.need_ack = 1;\n        }\n\n        ngx_quic_log_frame(c->log, f, 1);\n\n        flen = ngx_quic_create_frame(p, f);\n        if (flen == -1) {\n            return NGX_ERROR;\n        }\n\n        len += flen;\n        p += flen;\n\n        f->pnum = ctx->pnum;\n        f->first = now;\n        f->last = now;\n        f->plen = 0;\n\n        q = ngx_queue_next(q);\n\n        ngx_quic_queue_frame_remove(qc, fqueue->frames, f);\n        ngx_queue_insert_tail(&ctx->sending, &f->queue);\n\n        if (first == NULL) {\n            first = f;\n        }\n\n        fqueue->count++;\n\n        if (ngx_queue_empty(fqueue->frames)) {\n            ngx_queue_remove(&fqueue->queue);\n\n            fqueue->attached = 0;\n        } else if (fqueue != &ctx->fqueue && fqueue->count > qc->conf->stream_shuffle) {\n            ngx_queue_remove(&fqueue->queue);\n            ngx_queue_insert_tail(&ctx->fqueues, &fqueue->queue);\n\n            fqueue->count = 0;\n        }\n\n        nframes++;\n\n        if (f->flush) {\n            break;\n        }\n    }\n\n    if (nframes == 0) {\n        return 0;\n    }\n\n    if (len < min_payload) {\n        ngx_memset(p, NGX_QUIC_FT_PADDING, min_payload - len);\n        len = min_payload;\n    }\n\n    pkt.payload.data = src;\n    pkt.payload.len = len;\n\n    res.data = data;\n\n    ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic packet tx %s bytes:%ui\"\n                   \" need_ack:%d number:%L encoded nl:%d trunc:0x%xD\",\n                   ngx_quic_level_name(ctx->level), pkt.payload.len,\n                   pkt.need_ack, pkt.number, pkt.num_len, pkt.trunc);\n\n    if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ctx->pnum++;\n\n    if (pkt.need_ack) {\n        first->plen = res.len;\n    }\n\n    for (q = &first->queue; q != ngx_queue_sentinel(&ctx->sending); q = ngx_queue_next(q)) {\n        f = ngx_queue_data(q, ngx_quic_frame_t, queue);\n        f->pkt_need_ack = pkt.need_ack;\n    }\n\n    return res.len;\n}\n\n\nstatic void\nngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    ngx_quic_socket_t *qsock, ngx_quic_header_t *pkt)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    ngx_memzero(pkt, sizeof(ngx_quic_header_t));\n\n    pkt->flags = NGX_QUIC_PKT_FIXED_BIT;\n\n    if (ctx->level == ssl_encryption_initial) {\n        pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;\n\n    } else if (ctx->level == ssl_encryption_handshake) {\n        pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;\n\n    } else {\n        if (qc->key_phase) {\n            pkt->flags |= NGX_QUIC_PKT_KPHASE;\n        }\n    }\n\n    pkt->dcid.data = qsock->cid->id;\n    pkt->dcid.len = qsock->cid->len;\n\n    pkt->scid.data = qsock->sid.id;\n    pkt->scid.len = qsock->sid.len;\n\n    pkt->version = qc->version;\n    pkt->log = c->log;\n    pkt->level = ctx->level;\n\n    pkt->keys = qc->keys;\n\n    ngx_quic_set_packet_number(pkt, ctx);\n}\n\n\nstatic ssize_t\nngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len,\n    struct sockaddr *sockaddr, socklen_t socklen)\n{\n    ssize_t          n;\n    struct iovec     iov;\n    struct msghdr    msg;\n#if defined(NGX_HAVE_ADDRINFO_CMSG)\n    struct cmsghdr  *cmsg;\n    char             msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))];\n#endif\n\n    ngx_memzero(&msg, sizeof(struct msghdr));\n\n    iov.iov_len = len;\n    iov.iov_base = buf;\n\n    msg.msg_iov = &iov;\n    msg.msg_iovlen = 1;\n\n    msg.msg_name = sockaddr;\n    msg.msg_namelen = socklen;\n\n#if defined(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    n = ngx_sendmsg(c, &msg, 0);\n    if (n < 0) {\n        return n;\n    }\n\n    c->sent += n;\n\n    return n;\n}\n\n\nstatic void\nngx_quic_set_packet_number(ngx_quic_header_t *pkt, ngx_quic_send_ctx_t *ctx)\n{\n    uint64_t  delta;\n\n    delta = ctx->pnum - ctx->largest_ack;\n    pkt->number = ctx->pnum;\n\n    if (delta <= 0x7F) {\n        pkt->num_len = 1;\n        pkt->trunc = ctx->pnum & 0xff;\n\n    } else if (delta <= 0x7FFF) {\n        pkt->num_len = 2;\n        pkt->flags |= 0x1;\n        pkt->trunc = ctx->pnum & 0xffff;\n\n    } else if (delta <= 0x7FFFFF) {\n        pkt->num_len = 3;\n        pkt->flags |= 0x2;\n        pkt->trunc = ctx->pnum & 0xffffff;\n\n    } else {\n        pkt->num_len = 4;\n        pkt->flags |= 0x3;\n        pkt->trunc = ctx->pnum & 0xffffffff;\n    }\n}\n\n\nngx_int_t\nngx_quic_negotiate_version(ngx_connection_t *c, ngx_quic_header_t *inpkt)\n{\n    size_t             len;\n    ngx_quic_header_t  pkt;\n    static u_char      buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"sending version negotiation packet\");\n\n    pkt.log = c->log;\n    pkt.flags = NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_FIXED_BIT;\n    pkt.dcid = inpkt->scid;\n    pkt.scid = inpkt->dcid;\n\n    len = ngx_quic_create_version_negotiation(&pkt, buf);\n\n#ifdef NGX_QUIC_DEBUG_PACKETS\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic vnego packet to send len:%uz %*xs\", len, len, buf);\n#endif\n\n    (void) ngx_quic_send(c, buf, len, c->sockaddr, c->socklen);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf,\n    ngx_quic_header_t *pkt)\n{\n    u_char    *token;\n    size_t     len, max;\n    uint16_t   rndbytes;\n    u_char     buf[NGX_QUIC_MAX_SR_PACKET];\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic handle stateless reset output\");\n\n    if (pkt->len <= NGX_QUIC_MIN_PKT_LEN) {\n        return NGX_DECLINED;\n    }\n\n    if (pkt->len <= NGX_QUIC_MIN_SR_PACKET) {\n        len = pkt->len - 1;\n\n    } else {\n        max = ngx_min(NGX_QUIC_MAX_SR_PACKET, pkt->len * 3);\n\n        if (RAND_bytes((u_char *) &rndbytes, sizeof(rndbytes)) != 1) {\n            return NGX_ERROR;\n        }\n\n        len = (rndbytes % (max - NGX_QUIC_MIN_SR_PACKET + 1))\n              + NGX_QUIC_MIN_SR_PACKET;\n    }\n\n    if (RAND_bytes(buf, len - NGX_QUIC_SR_TOKEN_LEN) != 1) {\n        return NGX_ERROR;\n    }\n\n    buf[0] &= ~NGX_QUIC_PKT_LONG;\n    buf[0] |= NGX_QUIC_PKT_FIXED_BIT;\n\n    token = &buf[len - NGX_QUIC_SR_TOKEN_LEN];\n\n    if (ngx_quic_new_sr_token(c, &pkt->dcid, conf->sr_token_key, token)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_quic_send(c, buf, len, c->sockaddr, c->socklen);\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_quic_send_cc(ngx_connection_t *c)\n{\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc->draining) {\n        return NGX_OK;\n    }\n\n    if (qc->closing\n        && ngx_current_msec - qc->last_cc < NGX_QUIC_CC_MIN_INTERVAL)\n    {\n        /* dot not send CC too often */\n        return NGX_OK;\n    }\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = qc->error_level;\n    frame->type = qc->error_app ? NGX_QUIC_FT_CONNECTION_CLOSE_APP\n                                : NGX_QUIC_FT_CONNECTION_CLOSE;\n    frame->u.close.error_code = qc->error;\n    frame->u.close.frame_type = qc->error_ftype;\n\n    if (qc->error_reason) {\n        frame->u.close.reason.len = ngx_strlen(qc->error_reason);\n        frame->u.close.reason.data = (u_char *) qc->error_reason;\n    }\n\n    ngx_quic_queue_frame(qc, frame);\n\n    qc->last_cc = ngx_current_msec;\n\n    return ngx_quic_output(c);\n}\n\n\nngx_int_t\nngx_quic_send_early_cc(ngx_connection_t *c, ngx_quic_header_t *inpkt,\n    ngx_uint_t err, const char *reason)\n{\n    ssize_t            len;\n    ngx_str_t          res;\n    ngx_quic_frame_t   frame;\n    ngx_quic_header_t  pkt;\n\n    static u_char       src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n    static u_char       dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n\n    ngx_memzero(&frame, sizeof(ngx_quic_frame_t));\n    ngx_memzero(&pkt, sizeof(ngx_quic_header_t));\n\n    frame.level = inpkt->level;\n    frame.type = NGX_QUIC_FT_CONNECTION_CLOSE;\n    frame.u.close.error_code = err;\n\n    frame.u.close.reason.data = (u_char *) reason;\n    frame.u.close.reason.len = ngx_strlen(reason);\n\n    len = ngx_quic_create_frame(NULL, &frame);\n    if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) {\n        return NGX_ERROR;\n    }\n\n    ngx_quic_log_frame(c->log, &frame, 1);\n\n    len = ngx_quic_create_frame(src, &frame);\n    if (len == -1) {\n        return NGX_ERROR;\n    }\n\n    pkt.keys = ngx_quic_keys_new(c->pool);\n    if (pkt.keys == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_quic_keys_set_initial_secret(c->pool, pkt.keys, &inpkt->dcid,\n                                         inpkt->version)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG\n                | NGX_QUIC_PKT_INITIAL;\n\n    pkt.num_len = 1;\n    /*\n     * pkt.num = 0;\n     * pkt.trunc = 0;\n     */\n\n    pkt.version = inpkt->version;\n    pkt.log = c->log;\n    pkt.level = inpkt->level;\n    pkt.dcid = inpkt->scid;\n    pkt.scid = inpkt->dcid;\n    pkt.payload.data = src;\n    pkt.payload.len = len;\n\n    res.data = dst;\n\n    if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen) < 0) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_send_retry(ngx_connection_t *c, ngx_quic_conf_t *conf,\n    ngx_quic_header_t *inpkt)\n{\n    time_t             expires;\n    ssize_t            len;\n    ngx_str_t          res, token;\n    ngx_quic_header_t  pkt;\n\n    u_char             buf[NGX_QUIC_RETRY_BUFFER_SIZE];\n    u_char             dcid[NGX_QUIC_SERVER_CID_LEN];\n\n    expires = ngx_time() + NGX_QUIC_RETRY_TOKEN_LIFETIME;\n\n    if (ngx_quic_new_token(c, c->sockaddr, c->socklen, conf->av_token_key,\n                           &token, &inpkt->dcid, expires, 1)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&pkt, sizeof(ngx_quic_header_t));\n    pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY;\n    pkt.version = inpkt->version;\n    pkt.log = c->log;\n\n    pkt.odcid = inpkt->dcid;\n    pkt.dcid = inpkt->scid;\n\n    /* TODO: generate routable dcid */\n    if (RAND_bytes(dcid, NGX_QUIC_SERVER_CID_LEN) != 1) {\n        return NGX_ERROR;\n    }\n\n    pkt.scid.len = NGX_QUIC_SERVER_CID_LEN;\n    pkt.scid.data = dcid;\n\n    pkt.token = token;\n\n    res.data = buf;\n\n    if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n#ifdef NGX_QUIC_DEBUG_PACKETS\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic packet to send len:%uz %xV\", res.len, &res);\n#endif\n\n    len = ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen);\n    if (len < 0) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic retry packet sent to %xV\", &pkt.dcid);\n\n    /*\n     * RFC 9000, 17.2.5.1.  Sending a Retry Packet\n     *\n     * A server MUST NOT send more than one Retry\n     * packet in response to a single UDP datagram.\n     * NGX_DONE will stop quic_input() from processing further\n     */\n    return NGX_DONE;\n}\n\n\nngx_int_t\nngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path)\n{\n    time_t                  expires;\n    ngx_str_t               token;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME;\n\n    if (ngx_quic_new_token(c, path->sockaddr, path->socklen,\n                           qc->conf->av_token_key, &token, NULL, expires, 0)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_NEW_TOKEN;\n    frame->u.token.length = token.len;\n    frame->u.token.data = token.data;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_send_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)\n{\n    size_t                  len, left;\n    uint64_t                ack_delay;\n    ngx_buf_t              *b;\n    ngx_uint_t              i;\n    ngx_chain_t            *cl, **ll;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    ack_delay = ngx_current_msec - ctx->largest_received;\n    ack_delay *= 1000;\n    ack_delay >>= qc->tp.ack_delay_exponent;\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    ll = &frame->data;\n    b = NULL;\n\n    for (i = 0; i < ctx->nranges; i++) {\n        len = ngx_quic_create_ack_range(NULL, ctx->ranges[i].gap,\n                                        ctx->ranges[i].range);\n\n        left = b ? b->end - b->last : 0;\n\n        if (left < len) {\n            cl = ngx_quic_alloc_buf(c);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            *ll = cl;\n            ll = &cl->next;\n\n            b = cl->buf;\n            left = b->end - b->last;\n\n            if (left < len) {\n                return NGX_ERROR;\n            }\n        }\n\n        b->last += ngx_quic_create_ack_range(b->last, ctx->ranges[i].gap,\n                                             ctx->ranges[i].range);\n\n        frame->u.ack.ranges_length += len;\n    }\n\n    *ll = NULL;\n\n    frame->level = ctx->level;\n    frame->type = NGX_QUIC_FT_ACK;\n    frame->u.ack.largest = ctx->largest_range;\n    frame->u.ack.delay = ack_delay;\n    frame->u.ack.range_count = ctx->nranges;\n    frame->u.ack.first_range = ctx->first_range;\n\n    ngx_quic_queue_frame_priority(qc, frame, 1);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_send_ack_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,\n    uint64_t smallest, uint64_t largest)\n{\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ctx->level;\n    frame->type = NGX_QUIC_FT_ACK;\n    frame->u.ack.largest = largest;\n    frame->u.ack.delay = 0;\n    frame->u.ack.range_count = 0;\n    frame->u.ack.first_range = largest - smallest;\n\n    ngx_quic_queue_frame_priority(qc, frame, 1);\n\n    return NGX_OK;\n}\n\n\nssize_t\nngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,\n    size_t min, struct sockaddr *sockaddr, socklen_t socklen)\n{\n    size_t                  min_payload, pad;\n    ssize_t                 len;\n    ngx_str_t               res;\n    ngx_quic_header_t       pkt;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n\n    static u_char           src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n    static u_char           dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];\n\n    qc = ngx_quic_get_connection(c);\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);\n\n    ngx_quic_init_packet(c, ctx, qc->socket, &pkt);\n\n    min_payload = min ? ngx_quic_payload_size(&pkt, min) : 0;\n\n    pad = 4 - pkt.num_len;\n    min_payload = ngx_max(min_payload, pad);\n\n    len = ngx_quic_create_frame(NULL, frame);\n    if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) {\n        return -1;\n    }\n\n    ngx_quic_log_frame(c->log, frame, 1);\n\n    len = ngx_quic_create_frame(src, frame);\n    if (len == -1) {\n        return -1;\n    }\n\n    if (len < (ssize_t) min_payload) {\n        ngx_memset(src + len, NGX_QUIC_FT_PADDING, min_payload - len);\n        len = min_payload;\n     }\n\n    pkt.payload.data = src;\n    pkt.payload.len = len;\n\n    res.data = dst;\n\n    if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {\n        return -1;\n    }\n\n    frame->plen = res.len;\n    frame->pnum = ctx->pnum;\n    frame->first = ngx_current_msec;\n    frame->last = frame->first;\n\n    ctx->pnum++;\n\n    return ngx_quic_send(c, res.data, res.len, sockaddr, socklen);\n}\n\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\nssize_t\nngx_quic_frame_sendto_dont_fragment(ngx_connection_t *c, ngx_quic_frame_t *frame,\n    size_t min, struct sockaddr *sockaddr, socklen_t socklen)\n{\n    ssize_t    n;\n    int        optval = IP_PMTUDISC_DO, v6_only = 0;\n    socklen_t  v6_only_len = sizeof(v6_only);\n\n#if (NGX_HAVE_INET6)\n    if (c->sockaddr->sa_family == AF_INET6) {\n        if (setsockopt(c->fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &optval, sizeof(optval)) == -1) {\n            return NGX_ERROR;\n        }\n\n        if (getsockopt(c->fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6_only, &v6_only_len) != 0) {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    if (v6_only == 0 &&\n        setsockopt(c->fd, IPPROTO_IP, IP_MTU_DISCOVER, (const void *) &optval, sizeof(optval)) == -1)\n    {\n        return NGX_ERROR;\n    }\n\n    n = ngx_quic_frame_sendto(c, frame, min, sockaddr, socklen);\n\n    optval = IP_PMTUDISC_DONT;\n\n    if (!v6_only) {\n        setsockopt(c->fd, IPPROTO_IP, IP_MTU_DISCOVER, (const void *) &optval, sizeof(optval));\n    }\n\n#if (NGX_HAVE_INET6)\n    if (c->sockaddr->sa_family == AF_INET6 &&\n        setsockopt(c->fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &optval, sizeof(optval)) == -1)\n    {\n        return NGX_ERROR;\n    }\n#endif\n\n    return n;\n}\n#endif\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_output.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_\n#define _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nsize_t ngx_quic_max_udp_payload(ngx_connection_t *c);\n\nngx_int_t ngx_quic_output(ngx_connection_t *c);\n\nngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c,\n    ngx_quic_header_t *inpkt);\n\nngx_int_t ngx_quic_send_stateless_reset(ngx_connection_t *c,\n    ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);\nngx_int_t ngx_quic_send_cc(ngx_connection_t *c);\nngx_int_t ngx_quic_send_early_cc(ngx_connection_t *c,\n    ngx_quic_header_t *inpkt, ngx_uint_t err, const char *reason);\n\nngx_int_t ngx_quic_send_retry(ngx_connection_t *c,\n    ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);\nngx_int_t ngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path);\n\nngx_int_t ngx_quic_send_ack(ngx_connection_t *c,\n    ngx_quic_send_ctx_t *ctx);\nngx_int_t ngx_quic_send_ack_range(ngx_connection_t *c,\n    ngx_quic_send_ctx_t *ctx, uint64_t smallest, uint64_t largest);\n\nssize_t ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,\n    size_t min, struct sockaddr *sockaddr, socklen_t socklen);\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\nssize_t ngx_quic_frame_sendto_dont_fragment(ngx_connection_t *c, ngx_quic_frame_t *frame,\n    size_t min, struct sockaddr *sockaddr, socklen_t socklen);\n#endif\n\n#endif /* _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_protection.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#include <ngx_event_quic_connection.h>\n\n\n/* RFC 5116, 5.1 and RFC 8439, 2.3 for all supported ciphers */\n#define NGX_QUIC_IV_LEN               12\n/* RFC 9001, 5.4.1.  Header Protection Application: 5-byte mask */\n#define NGX_QUIC_HP_LEN               5\n\n#define NGX_QUIC_AES_128_KEY_LEN      16\n\n#define NGX_AES_128_GCM_SHA256        0x1301\n#define NGX_AES_256_GCM_SHA384        0x1302\n#define NGX_CHACHA20_POLY1305_SHA256  0x1303\n\n\n#ifdef OPENSSL_IS_BORINGSSL\n#define ngx_quic_cipher_t             EVP_AEAD\n#else\n#define ngx_quic_cipher_t             EVP_CIPHER\n#endif\n\n\ntypedef struct {\n    const ngx_quic_cipher_t  *c;\n    const EVP_CIPHER         *hp;\n    const EVP_MD             *d;\n} ngx_quic_ciphers_t;\n\n\ntypedef struct ngx_quic_secret_s {\n    ngx_str_t                 secret;\n    ngx_str_t                 key;\n    ngx_str_t                 iv;\n    ngx_str_t                 hp;\n} ngx_quic_secret_t;\n\n\ntypedef struct {\n    ngx_quic_secret_t         client;\n    ngx_quic_secret_t         server;\n} ngx_quic_secrets_t;\n\n\nstruct ngx_quic_keys_s {\n    ngx_quic_secrets_t        secrets[NGX_QUIC_ENCRYPTION_LAST];\n    ngx_quic_secrets_t        next_key;\n    ngx_uint_t                cipher;\n};\n\n\nstatic ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len,\n    const EVP_MD *digest, const u_char *prk, size_t prk_len,\n    const u_char *info, size_t info_len);\nstatic ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len,\n    const EVP_MD *digest, const u_char *secret, size_t secret_len,\n    const u_char *salt, size_t salt_len);\n\nstatic uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,\n    uint64_t *largest_pn);\nstatic void ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn);\nstatic ngx_int_t ngx_quic_ciphers(ngx_uint_t id,\n    ngx_quic_ciphers_t *ciphers, enum ssl_encryption_level_t level);\n\nstatic ngx_int_t ngx_quic_tls_open(const ngx_quic_cipher_t *cipher,\n    ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,\n    ngx_str_t *ad, ngx_log_t *log);\nstatic ngx_int_t ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher,\n    ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in,\n    ngx_str_t *ad, ngx_log_t *log);\nstatic ngx_int_t ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher,\n    ngx_quic_secret_t *s, u_char *out, u_char *in);\nstatic ngx_int_t ngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest,\n    ngx_str_t *out, ngx_str_t *label, const uint8_t *prk, size_t prk_len);\n\nstatic ngx_int_t ngx_quic_create_packet(ngx_quic_header_t *pkt,\n    ngx_str_t *res);\nstatic ngx_int_t ngx_quic_create_retry_packet(ngx_quic_header_t *pkt,\n    ngx_str_t *res);\n\n\nstatic ngx_int_t\nngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers,\n    enum ssl_encryption_level_t level)\n{\n    ngx_int_t  len;\n\n    if (level == ssl_encryption_initial) {\n        id = NGX_AES_128_GCM_SHA256;\n    }\n\n    switch (id) {\n\n    case NGX_AES_128_GCM_SHA256:\n#ifdef OPENSSL_IS_BORINGSSL\n        ciphers->c = EVP_aead_aes_128_gcm();\n#else\n        ciphers->c = EVP_aes_128_gcm();\n#endif\n        ciphers->hp = EVP_aes_128_ctr();\n        ciphers->d = EVP_sha256();\n        len = 16;\n        break;\n\n    case NGX_AES_256_GCM_SHA384:\n#ifdef OPENSSL_IS_BORINGSSL\n        ciphers->c = EVP_aead_aes_256_gcm();\n#else\n        ciphers->c = EVP_aes_256_gcm();\n#endif\n        ciphers->hp = EVP_aes_256_ctr();\n        ciphers->d = EVP_sha384();\n        len = 32;\n        break;\n\n    case NGX_CHACHA20_POLY1305_SHA256:\n#ifdef OPENSSL_IS_BORINGSSL\n        ciphers->c = EVP_aead_chacha20_poly1305();\n#else\n        ciphers->c = EVP_chacha20_poly1305();\n#endif\n#ifdef OPENSSL_IS_BORINGSSL\n        ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305();\n#else\n        ciphers->hp = EVP_chacha20();\n#endif\n        ciphers->d = EVP_sha256();\n        len = 32;\n        break;\n\n    default:\n        return NGX_ERROR;\n    }\n\n    return len;\n}\n\n\nngx_int_t\nngx_quic_keys_set_initial_secret(ngx_pool_t *pool, ngx_quic_keys_t *keys,\n    ngx_str_t *secret, uint32_t version)\n{\n    size_t              is_len;\n    uint8_t             is[SHA256_DIGEST_LENGTH];\n    ngx_uint_t          i;\n    const EVP_MD       *digest;\n    ngx_quic_secret_t  *client, *server;\n\n    static const uint8_t salt[20] =\n        \"\\x38\\x76\\x2c\\xf7\\xf5\\x59\\x34\\xb3\\x4d\\x17\"\n        \"\\x9a\\xe6\\xa4\\xc8\\x0c\\xad\\xcc\\xbb\\x7f\\x0a\";\n    static const uint8_t salt29[20] =\n        \"\\xaf\\xbf\\xec\\x28\\x99\\x93\\xd2\\x4c\\x9e\\x97\"\n        \"\\x86\\xf1\\x9c\\x61\\x11\\xe0\\x43\\x90\\xa8\\x99\";\n\n    client = &keys->secrets[ssl_encryption_initial].client;\n    server = &keys->secrets[ssl_encryption_initial].server;\n\n    /*\n     * RFC 9001, section 5.  Packet Protection\n     *\n     * Initial packets use AEAD_AES_128_GCM.  The hash function\n     * for HKDF when deriving initial secrets and keys is SHA-256.\n     */\n\n    digest = EVP_sha256();\n    is_len = SHA256_DIGEST_LENGTH;\n\n    if (ngx_hkdf_extract(is, &is_len, digest, secret->data, secret->len,\n                         (version & 0xff000000) ? salt29 : salt, sizeof(salt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_str_t iss = {\n        .data = is,\n        .len = is_len\n    };\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pool->log, 0,\n                   \"quic ngx_quic_set_initial_secret\");\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pool->log, 0,\n                   \"quic salt len:%uz %*xs\", sizeof(salt), sizeof(salt), salt);\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pool->log, 0,\n                   \"quic initial secret len:%uz %*xs\", is_len, is_len, is);\n#endif\n\n    client->secret.len = SHA256_DIGEST_LENGTH;\n    server->secret.len = SHA256_DIGEST_LENGTH;\n\n    client->key.len = NGX_QUIC_AES_128_KEY_LEN;\n    server->key.len = NGX_QUIC_AES_128_KEY_LEN;\n\n    client->hp.len = NGX_QUIC_AES_128_KEY_LEN;\n    server->hp.len = NGX_QUIC_AES_128_KEY_LEN;\n\n    client->iv.len = NGX_QUIC_IV_LEN;\n    server->iv.len = NGX_QUIC_IV_LEN;\n\n    struct {\n        ngx_str_t   label;\n        ngx_str_t  *key;\n        ngx_str_t  *prk;\n    } seq[] = {\n        /* labels per RFC 9001, 5.1. Packet Protection Keys */\n        { ngx_string(\"tls13 client in\"), &client->secret, &iss },\n        { ngx_string(\"tls13 quic key\"),  &client->key,    &client->secret },\n        { ngx_string(\"tls13 quic iv\"),   &client->iv,     &client->secret },\n        { ngx_string(\"tls13 quic hp\"),   &client->hp,     &client->secret },\n        { ngx_string(\"tls13 server in\"), &server->secret, &iss },\n        { ngx_string(\"tls13 quic key\"),  &server->key,    &server->secret },\n        { ngx_string(\"tls13 quic iv\"),   &server->iv,     &server->secret },\n        { ngx_string(\"tls13 quic hp\"),   &server->hp,     &server->secret },\n    };\n\n    for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {\n\n        if (ngx_quic_hkdf_expand(pool, digest, seq[i].key, &seq[i].label,\n                                 seq[i].prk->data, seq[i].prk->len)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_hkdf_expand(ngx_pool_t *pool, const EVP_MD *digest, ngx_str_t *out,\n    ngx_str_t *label, const uint8_t *prk, size_t prk_len)\n{\n    size_t    info_len;\n    uint8_t  *p;\n    uint8_t   info[20];\n\n    if (out->data == NULL) {\n        out->data = ngx_pnalloc(pool, out->len);\n        if (out->data == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    info_len = 2 + 1 + label->len + 1;\n\n    info[0] = 0;\n    info[1] = out->len;\n    info[2] = label->len;\n    p = ngx_cpymem(&info[3], label->data, label->len);\n    *p = '\\0';\n\n    if (ngx_hkdf_expand(out->data, out->len, digest,\n                        prk, prk_len, info, info_len)\n        != NGX_OK)\n    {\n        ngx_ssl_error(NGX_LOG_INFO, pool->log, 0,\n                      \"ngx_hkdf_expand(%V) failed\", label);\n        return NGX_ERROR;\n    }\n\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pool->log, 0,\n                   \"quic expand %V key len:%uz %xV\", label, out->len, out);\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest,\n    const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len)\n{\n#ifdef OPENSSL_IS_BORINGSSL\n\n    if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)\n        == 0)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n\n#else\n\n    EVP_PKEY_CTX  *pctx;\n\n    pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);\n    if (pctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (EVP_PKEY_derive_init(pctx) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_set1_hkdf_key(pctx, prk, prk_len) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_derive(pctx, out_key, &out_len) <= 0) {\n        goto failed;\n    }\n\n    EVP_PKEY_CTX_free(pctx);\n\n    return NGX_OK;\n\nfailed:\n\n    EVP_PKEY_CTX_free(pctx);\n\n    return NGX_ERROR;\n\n#endif\n}\n\n\nstatic ngx_int_t\nngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest,\n    const u_char *secret, size_t secret_len, const u_char *salt,\n    size_t salt_len)\n{\n#ifdef OPENSSL_IS_BORINGSSL\n\n    if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt,\n                     salt_len)\n        == 0)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n\n#else\n\n    EVP_PKEY_CTX  *pctx;\n\n    pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);\n    if (pctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (EVP_PKEY_derive_init(pctx) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_set_hkdf_md(pctx, digest) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, secret_len) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, salt_len) <= 0) {\n        goto failed;\n    }\n\n    if (EVP_PKEY_derive(pctx, out_key, out_len) <= 0) {\n        goto failed;\n    }\n\n    EVP_PKEY_CTX_free(pctx);\n\n    return NGX_OK;\n\nfailed:\n\n    EVP_PKEY_CTX_free(pctx);\n\n    return NGX_ERROR;\n\n#endif\n}\n\n\nstatic ngx_int_t\nngx_quic_tls_open(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,\n    ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad,\n    ngx_log_t *log)\n{\n\n#ifdef OPENSSL_IS_BORINGSSL\n    EVP_AEAD_CTX  *ctx;\n\n    ctx = EVP_AEAD_CTX_new(cipher, s->key.data, s->key.len,\n                           EVP_AEAD_DEFAULT_TAG_LENGTH);\n    if (ctx == NULL) {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_AEAD_CTX_new() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_AEAD_CTX_open(ctx, out->data, &out->len, out->len, nonce, s->iv.len,\n                          in->data, in->len, ad->data, ad->len)\n        != 1)\n    {\n        EVP_AEAD_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_AEAD_CTX_open() failed\");\n        return NGX_ERROR;\n    }\n\n    EVP_AEAD_CTX_free(ctx);\n#else\n    int              len;\n    u_char          *tag;\n    EVP_CIPHER_CTX  *ctx;\n\n    ctx = EVP_CIPHER_CTX_new();\n    if (ctx == NULL) {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_CIPHER_CTX_new() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_DecryptInit_ex() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, s->iv.len, NULL)\n        == 0)\n    {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0,\n                      \"EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_DecryptInit_ex(ctx, NULL, NULL, s->key.data, nonce) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_DecryptInit_ex() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_DecryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_DecryptUpdate() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_DecryptUpdate(ctx, out->data, &len, in->data,\n                          in->len - EVP_GCM_TLS_TAG_LEN)\n        != 1)\n    {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_DecryptUpdate() failed\");\n        return NGX_ERROR;\n    }\n\n    out->len = len;\n    tag = in->data + in->len - EVP_GCM_TLS_TAG_LEN;\n\n    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, EVP_GCM_TLS_TAG_LEN, tag)\n        == 0)\n    {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0,\n                      \"EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_TAG) failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_DecryptFinal_ex(ctx, out->data + len, &len) <= 0) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_DecryptFinal_ex failed\");\n        return NGX_ERROR;\n    }\n\n    out->len += len;\n\n    EVP_CIPHER_CTX_free(ctx);\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_tls_seal(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,\n    ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)\n{\n\n#ifdef OPENSSL_IS_BORINGSSL\n    EVP_AEAD_CTX  *ctx;\n\n    ctx = EVP_AEAD_CTX_new(cipher, s->key.data, s->key.len,\n                           EVP_AEAD_DEFAULT_TAG_LENGTH);\n    if (ctx == NULL) {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_AEAD_CTX_new() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_AEAD_CTX_seal(ctx, out->data, &out->len, out->len, nonce, s->iv.len,\n                          in->data, in->len, ad->data, ad->len)\n        != 1)\n    {\n        EVP_AEAD_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_AEAD_CTX_seal() failed\");\n        return NGX_ERROR;\n    }\n\n    EVP_AEAD_CTX_free(ctx);\n#else\n    int              len;\n    EVP_CIPHER_CTX  *ctx;\n\n    ctx = EVP_CIPHER_CTX_new();\n    if (ctx == NULL) {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_CIPHER_CTX_new() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptInit_ex() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, s->iv.len, NULL)\n        == 0)\n    {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0,\n                      \"EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_SET_IVLEN) failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_EncryptInit_ex(ctx, NULL, NULL, s->key.data, nonce) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptInit_ex() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_EncryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptUpdate() failed\");\n        return NGX_ERROR;\n    }\n\n    if (EVP_EncryptUpdate(ctx, out->data, &len, in->data, in->len) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptUpdate() failed\");\n        return NGX_ERROR;\n    }\n\n    out->len = len;\n\n    if (EVP_EncryptFinal_ex(ctx, out->data + out->len, &len) <= 0) {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptFinal_ex failed\");\n        return NGX_ERROR;\n    }\n\n    out->len += len;\n\n    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, EVP_GCM_TLS_TAG_LEN,\n                            out->data + in->len)\n        == 0)\n    {\n        EVP_CIPHER_CTX_free(ctx);\n        ngx_ssl_error(NGX_LOG_INFO, log, 0,\n                      \"EVP_CIPHER_CTX_ctrl(EVP_CTRL_GCM_GET_TAG) failed\");\n        return NGX_ERROR;\n    }\n\n    EVP_CIPHER_CTX_free(ctx);\n\n    out->len += EVP_GCM_TLS_TAG_LEN;\n#endif\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher,\n    ngx_quic_secret_t *s, u_char *out, u_char *in)\n{\n    int              outlen;\n    EVP_CIPHER_CTX  *ctx;\n    u_char           zero[NGX_QUIC_HP_LEN] = {0};\n\n#ifdef OPENSSL_IS_BORINGSSL\n    uint32_t         cnt;\n\n    ngx_memcpy(&cnt, in, sizeof(uint32_t));\n\n    if (cipher == (const EVP_CIPHER *) EVP_aead_chacha20_poly1305()) {\n        CRYPTO_chacha_20(out, zero, NGX_QUIC_HP_LEN, s->hp.data, &in[4], cnt);\n        return NGX_OK;\n    }\n#endif\n\n    ctx = EVP_CIPHER_CTX_new();\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (EVP_EncryptInit_ex(ctx, cipher, NULL, s->hp.data, in) != 1) {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptInit_ex() failed\");\n        goto failed;\n    }\n\n    if (!EVP_EncryptUpdate(ctx, out, &outlen, zero, NGX_QUIC_HP_LEN)) {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptUpdate() failed\");\n        goto failed;\n    }\n\n    if (!EVP_EncryptFinal_ex(ctx, out + NGX_QUIC_HP_LEN, &outlen)) {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0, \"EVP_EncryptFinal_Ex() failed\");\n        goto failed;\n    }\n\n    EVP_CIPHER_CTX_free(ctx);\n\n    return NGX_OK;\n\nfailed:\n\n    EVP_CIPHER_CTX_free(ctx);\n\n    return NGX_ERROR;\n}\n\n\nint ngx_quic_keys_set_encryption_secret(ngx_pool_t *pool, ngx_uint_t is_write,\n    ngx_quic_keys_t *keys, enum ssl_encryption_level_t level,\n    const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)\n{\n    ngx_int_t            key_len;\n    ngx_uint_t           i;\n    ngx_quic_secret_t   *peer_secret;\n    ngx_quic_ciphers_t   ciphers;\n\n    peer_secret = is_write ? &keys->secrets[level].server\n                           : &keys->secrets[level].client;\n\n    keys->cipher = SSL_CIPHER_get_protocol_id(cipher);\n\n    key_len = ngx_quic_ciphers(keys->cipher, &ciphers, level);\n\n    if (key_len == NGX_ERROR) {\n        ngx_ssl_error(NGX_LOG_INFO, pool->log, 0, \"unexpected cipher\");\n        return 0;\n    }\n\n    if (level == ssl_encryption_initial) {\n        return 0;\n    }\n\n    peer_secret->secret.data = ngx_pnalloc(pool, secret_len);\n    if (peer_secret->secret.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    peer_secret->secret.len = secret_len;\n    ngx_memcpy(peer_secret->secret.data, secret, secret_len);\n\n    peer_secret->key.len = key_len;\n    peer_secret->iv.len = NGX_QUIC_IV_LEN;\n    peer_secret->hp.len = key_len;\n\n    struct {\n        ngx_str_t       label;\n        ngx_str_t      *key;\n        const uint8_t  *secret;\n    } seq[] = {\n        { ngx_string(\"tls13 quic key\"), &peer_secret->key, secret },\n        { ngx_string(\"tls13 quic iv\"),  &peer_secret->iv,  secret },\n        { ngx_string(\"tls13 quic hp\"),  &peer_secret->hp,  secret },\n    };\n\n    for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {\n\n        if (ngx_quic_hkdf_expand(pool, ciphers.d, seq[i].key, &seq[i].label,\n                                 seq[i].secret, secret_len)\n            != NGX_OK)\n        {\n            return 0;\n        }\n    }\n\n    return 1;\n}\n\n\nngx_quic_keys_t *\nngx_quic_keys_new(ngx_pool_t *pool)\n{\n    return ngx_pcalloc(pool, sizeof(ngx_quic_keys_t));\n}\n\n\nngx_uint_t\nngx_quic_keys_available(ngx_quic_keys_t *keys,\n    enum ssl_encryption_level_t level)\n{\n    return keys->secrets[level].client.key.len != 0;\n}\n\n\nvoid\nngx_quic_keys_discard(ngx_quic_keys_t *keys,\n    enum ssl_encryption_level_t level)\n{\n    keys->secrets[level].client.key.len = 0;\n}\n\n\nvoid\nngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys)\n{\n    ngx_quic_secrets_t  *current, *next, tmp;\n\n    current = &keys->secrets[ssl_encryption_application];\n    next = &keys->next_key;\n\n    tmp = *current;\n    *current = *next;\n    *next = tmp;\n}\n\n\nngx_int_t\nngx_quic_keys_update(ngx_connection_t *c, ngx_quic_keys_t *keys)\n{\n    ngx_uint_t           i;\n    ngx_quic_ciphers_t   ciphers;\n    ngx_quic_secrets_t  *current, *next;\n\n    current = &keys->secrets[ssl_encryption_application];\n    next = &keys->next_key;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic key update\");\n\n    if (ngx_quic_ciphers(keys->cipher, &ciphers, ssl_encryption_application)\n        == NGX_ERROR)\n    {\n        return NGX_ERROR;\n    }\n\n    next->client.secret.len = current->client.secret.len;\n    next->client.key.len = current->client.key.len;\n    next->client.iv.len = NGX_QUIC_IV_LEN;\n    next->client.hp = current->client.hp;\n\n    next->server.secret.len = current->server.secret.len;\n    next->server.key.len = current->server.key.len;\n    next->server.iv.len = NGX_QUIC_IV_LEN;\n    next->server.hp = current->server.hp;\n\n    struct {\n        ngx_str_t   label;\n        ngx_str_t  *key;\n        ngx_str_t  *secret;\n    } seq[] = {\n        {\n            ngx_string(\"tls13 quic ku\"),\n            &next->client.secret,\n            &current->client.secret,\n        },\n        {\n            ngx_string(\"tls13 quic key\"),\n            &next->client.key,\n            &next->client.secret,\n        },\n        {\n            ngx_string(\"tls13 quic iv\"),\n            &next->client.iv,\n            &next->client.secret,\n        },\n        {\n            ngx_string(\"tls13 quic ku\"),\n            &next->server.secret,\n            &current->server.secret,\n        },\n        {\n            ngx_string(\"tls13 quic key\"),\n            &next->server.key,\n            &next->server.secret,\n        },\n        {\n            ngx_string(\"tls13 quic iv\"),\n            &next->server.iv,\n            &next->server.secret,\n        },\n    };\n\n    for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) {\n\n        if (ngx_quic_hkdf_expand(c->pool, ciphers.d, seq[i].key, &seq[i].label,\n                                 seq[i].secret->data, seq[i].secret->len)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_create_packet(ngx_quic_header_t *pkt, ngx_str_t *res)\n{\n    u_char              *pnp, *sample;\n    ngx_str_t            ad, out;\n    ngx_uint_t           i;\n    ngx_quic_secret_t   *secret;\n    ngx_quic_ciphers_t   ciphers;\n    u_char               nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN];\n\n    ad.data = res->data;\n    ad.len = ngx_quic_create_header(pkt, ad.data, &pnp);\n\n    out.len = pkt->payload.len + EVP_GCM_TLS_TAG_LEN;\n    out.data = res->data + ad.len;\n\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic ad len:%uz %xV\", ad.len, &ad);\n#endif\n\n    if (ngx_quic_ciphers(pkt->keys->cipher, &ciphers, pkt->level) == NGX_ERROR)\n    {\n        return NGX_ERROR;\n    }\n\n    secret = &pkt->keys->secrets[pkt->level].server;\n\n    ngx_memcpy(nonce, secret->iv.data, secret->iv.len);\n    ngx_quic_compute_nonce(nonce, sizeof(nonce), pkt->number);\n\n    if (ngx_quic_tls_seal(ciphers.c, secret, &out,\n                          nonce, &pkt->payload, &ad, pkt->log)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    sample = &out.data[4 - pkt->num_len];\n    if (ngx_quic_tls_hp(pkt->log, ciphers.hp, secret, mask, sample)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    /* RFC 9001, 5.4.1.  Header Protection Application */\n    ad.data[0] ^= mask[0] & ngx_quic_pkt_hp_mask(pkt->flags);\n\n    for (i = 0; i < pkt->num_len; i++) {\n        pnp[i] ^= mask[i + 1];\n    }\n\n    res->len = ad.len + out.len;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)\n{\n    u_char              *start;\n    ngx_str_t            ad, itag;\n    ngx_quic_secret_t    secret;\n    ngx_quic_ciphers_t   ciphers;\n\n    /* 5.8.  Retry Packet Integrity */\n    static u_char     key[16] =\n        \"\\xbe\\x0c\\x69\\x0b\\x9f\\x66\\x57\\x5a\\x1d\\x76\\x6b\\x54\\xe3\\x68\\xc8\\x4e\";\n    static u_char     key29[16] =\n        \"\\xcc\\xce\\x18\\x7e\\xd0\\x9a\\x09\\xd0\\x57\\x28\\x15\\x5a\\x6c\\xb9\\x6b\\xe1\";\n    static u_char     nonce[NGX_QUIC_IV_LEN] =\n        \"\\x46\\x15\\x99\\xd3\\x5d\\x63\\x2b\\xf2\\x23\\x98\\x25\\xbb\";\n    static u_char     nonce29[NGX_QUIC_IV_LEN] =\n        \"\\xe5\\x49\\x30\\xf9\\x7f\\x21\\x36\\xf0\\x53\\x0a\\x8c\\x1c\";\n    static ngx_str_t  in = ngx_string(\"\");\n\n    ad.data = res->data;\n    ad.len = ngx_quic_create_retry_itag(pkt, ad.data, &start);\n\n    itag.data = ad.data + ad.len;\n    itag.len = EVP_GCM_TLS_TAG_LEN;\n\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic retry itag len:%uz %xV\", ad.len, &ad);\n#endif\n\n    if (ngx_quic_ciphers(0, &ciphers, pkt->level) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    secret.key.len = sizeof(key);\n    secret.key.data = (pkt->version & 0xff000000) ? key29 : key;\n    secret.iv.len = NGX_QUIC_IV_LEN;\n\n    if (ngx_quic_tls_seal(ciphers.c, &secret, &itag,\n                          (pkt->version & 0xff000000) ? nonce29 : nonce,\n                          &in, &ad, pkt->log)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    res->len = itag.data + itag.len - start;\n    res->data = start;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_derive_key(ngx_log_t *log, const char *label, ngx_str_t *secret,\n    ngx_str_t *salt, u_char *out, size_t len)\n{\n    size_t         is_len, info_len;\n    uint8_t       *p;\n    const EVP_MD  *digest;\n\n    uint8_t        is[SHA256_DIGEST_LENGTH];\n    uint8_t        info[20];\n\n    digest = EVP_sha256();\n    is_len = SHA256_DIGEST_LENGTH;\n\n    if (ngx_hkdf_extract(is, &is_len, digest, secret->data, secret->len,\n                         salt->data, salt->len)\n        != NGX_OK)\n    {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0,\n                      \"ngx_hkdf_extract(%s) failed\", label);\n        return NGX_ERROR;\n    }\n\n    info[0] = 0;\n    info[1] = len;\n    info[2] = ngx_strlen(label);\n\n    info_len = 2 + 1 + info[2] + 1;\n\n    if (info_len >= 20) {\n        ngx_log_error(NGX_LOG_INFO, log, 0,\n                      \"ngx_quic_create_key label \\\"%s\\\" too long\", label);\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(&info[3], label, info[2]);\n    *p = '\\0';\n\n    if (ngx_hkdf_expand(out, len, digest, is, is_len, info, info_len) != NGX_OK)\n    {\n        ngx_ssl_error(NGX_LOG_INFO, log, 0,\n                      \"ngx_hkdf_expand(%s) failed\", label);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic uint64_t\nngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,\n    uint64_t *largest_pn)\n{\n    u_char    *p;\n    uint64_t   truncated_pn, expected_pn, candidate_pn;\n    uint64_t   pn_nbits, pn_win, pn_hwin, pn_mask;\n\n    pn_nbits = ngx_min(len * 8, 62);\n\n    p = *pos;\n    truncated_pn = *p++ ^ *mask++;\n\n    while (--len) {\n        truncated_pn = (truncated_pn << 8) + (*p++ ^ *mask++);\n    }\n\n    *pos = p;\n\n    expected_pn = *largest_pn + 1;\n    pn_win = 1ULL << pn_nbits;\n    pn_hwin = pn_win / 2;\n    pn_mask = pn_win - 1;\n\n    candidate_pn = (expected_pn & ~pn_mask) | truncated_pn;\n\n    if ((int64_t) candidate_pn <= (int64_t) (expected_pn - pn_hwin)\n        && candidate_pn < (1ULL << 62) - pn_win)\n    {\n        candidate_pn += pn_win;\n\n    } else if (candidate_pn > expected_pn + pn_hwin\n               && candidate_pn >= pn_win)\n    {\n        candidate_pn -= pn_win;\n    }\n\n    *largest_pn = ngx_max((int64_t) *largest_pn, (int64_t) candidate_pn);\n\n    return candidate_pn;\n}\n\n\nstatic void\nngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn)\n{\n    nonce[len - 4] ^= (pn & 0xff000000) >> 24;\n    nonce[len - 3] ^= (pn & 0x00ff0000) >> 16;\n    nonce[len - 2] ^= (pn & 0x0000ff00) >> 8;\n    nonce[len - 1] ^= (pn & 0x000000ff);\n}\n\n\nngx_int_t\nngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_str_t *res)\n{\n    if (ngx_quic_pkt_retry(pkt->flags)) {\n        return ngx_quic_create_retry_packet(pkt, res);\n    }\n\n    return ngx_quic_create_packet(pkt, res);\n}\n\n\nngx_int_t\nngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn)\n{\n    u_char              *p, *sample;\n    size_t               len;\n    uint64_t             pn, lpn;\n    ngx_int_t            pnl, rc, key_phase;\n    ngx_str_t            in, ad;\n    ngx_quic_secret_t   *secret;\n    ngx_quic_ciphers_t   ciphers;\n    uint8_t              nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN];\n\n    if (ngx_quic_ciphers(pkt->keys->cipher, &ciphers, pkt->level) == NGX_ERROR)\n    {\n        return NGX_ERROR;\n    }\n\n    secret = &pkt->keys->secrets[pkt->level].client;\n\n    p = pkt->raw->pos;\n    len = pkt->data + pkt->len - p;\n\n    /*\n     * RFC 9001, 5.4.2. Header Protection Sample\n     *           5.4.3. AES-Based Header Protection\n     *           5.4.4. ChaCha20-Based Header Protection\n     *\n     * the Packet Number field is assumed to be 4 bytes long\n     * AES and ChaCha20 algorithms sample 16 bytes\n     */\n\n    if (len < EVP_GCM_TLS_TAG_LEN + 4) {\n        return NGX_DECLINED;\n    }\n\n    sample = p + 4;\n\n    /* header protection */\n\n    if (ngx_quic_tls_hp(pkt->log, ciphers.hp, secret, mask, sample)\n        != NGX_OK)\n    {\n        return NGX_DECLINED;\n    }\n\n    pkt->flags ^= mask[0] & ngx_quic_pkt_hp_mask(pkt->flags);\n\n    if (ngx_quic_short_pkt(pkt->flags)) {\n        key_phase = (pkt->flags & NGX_QUIC_PKT_KPHASE) != 0;\n\n        if (key_phase != pkt->key_phase) {\n            secret = &pkt->keys->next_key.client;\n            pkt->key_update = 1;\n        }\n    }\n\n    lpn = *largest_pn;\n\n    pnl = (pkt->flags & 0x03) + 1;\n    pn = ngx_quic_parse_pn(&p, pnl, &mask[1], &lpn);\n\n    pkt->pn = pn;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic packet rx clearflags:%xd\", pkt->flags);\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic packet rx number:%uL len:%xi\", pn, pnl);\n\n    /* packet protection */\n\n    in.data = p;\n    in.len = len - pnl;\n\n    ad.len = p - pkt->data;\n    ad.data = pkt->plaintext;\n\n    ngx_memcpy(ad.data, pkt->data, ad.len);\n    ad.data[0] = pkt->flags;\n\n    do {\n        ad.data[ad.len - pnl] = pn >> (8 * (pnl - 1)) % 256;\n    } while (--pnl);\n\n    ngx_memcpy(nonce, secret->iv.data, secret->iv.len);\n    ngx_quic_compute_nonce(nonce, sizeof(nonce), pn);\n\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic ad len:%uz %xV\", ad.len, &ad);\n#endif\n\n    pkt->payload.len = in.len - EVP_GCM_TLS_TAG_LEN;\n    pkt->payload.data = pkt->plaintext + ad.len;\n\n    rc = ngx_quic_tls_open(ciphers.c, secret, &pkt->payload,\n                           nonce, &in, &ad, pkt->log);\n    if (rc != NGX_OK) {\n        return NGX_DECLINED;\n    }\n\n    if (pkt->payload.len == 0) {\n        /*\n         * RFC 9000, 12.4.  Frames and Frame Types\n         *\n         * An endpoint MUST treat receipt of a packet containing no\n         * frames as a connection error of type PROTOCOL_VIOLATION.\n         */\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0, \"quic zero-length packet\");\n        pkt->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;\n        return NGX_ERROR;\n    }\n\n    if (pkt->flags & ngx_quic_pkt_rb_mask(pkt->flags)) {\n        /*\n         * RFC 9000, Reserved Bits\n         *\n         * An endpoint MUST treat receipt of a packet that has\n         * a non-zero value for these bits, after removing both\n         * packet and header protection, as a connection error\n         * of type PROTOCOL_VIOLATION.\n         */\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic reserved bit set in packet\");\n        pkt->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;\n        return NGX_ERROR;\n    }\n\n#if defined(NGX_QUIC_DEBUG_CRYPTO) && defined(NGX_QUIC_DEBUG_PACKETS)\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic packet payload len:%uz %xV\",\n                   pkt->payload.len, &pkt->payload);\n#endif\n\n    *largest_pn = lpn;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_protection.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_\n#define _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#include <ngx_event_quic_transport.h>\n\n\n#define NGX_QUIC_ENCRYPTION_LAST  ((ssl_encryption_application) + 1)\n\n\nngx_quic_keys_t *ngx_quic_keys_new(ngx_pool_t *pool);\nngx_int_t ngx_quic_keys_set_initial_secret(ngx_pool_t *pool,\n    ngx_quic_keys_t *keys, ngx_str_t *secret, uint32_t version);\nint ngx_quic_keys_set_encryption_secret(ngx_pool_t *pool, ngx_uint_t is_write,\n    ngx_quic_keys_t *keys, enum ssl_encryption_level_t level,\n    const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);\nngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys,\n    enum ssl_encryption_level_t level);\nvoid ngx_quic_keys_discard(ngx_quic_keys_t *keys,\n    enum ssl_encryption_level_t level);\nvoid ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);\nngx_int_t ngx_quic_keys_update(ngx_connection_t *c, ngx_quic_keys_t *keys);\nngx_int_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_str_t *res);\nngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn);\n\n\n#endif /* _NGX_EVENT_QUIC_PROTECTION_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_socket.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#include <ngx_event_quic_connection.h>\n\n\nstatic ngx_int_t ngx_quic_create_temp_socket(ngx_connection_t *c,\n    ngx_quic_connection_t *qc, ngx_str_t *dcid, ngx_quic_path_t *path,\n    ngx_quic_client_id_t *cid);\n\n\nngx_int_t\nngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,\n    ngx_quic_header_t *pkt)\n{\n    ngx_quic_path_t       *path;\n    ngx_quic_socket_t     *qsock;\n    ngx_quic_client_id_t  *cid;\n\n    /*\n     * qc->nclient_ids = 0\n     * qc->nsockets = 0\n     * qc->max_retired_seqnum = 0\n     * qc->client_seqnum = 0\n     */\n\n    ngx_queue_init(&qc->sockets);\n    ngx_queue_init(&qc->free_sockets);\n\n    ngx_queue_init(&qc->paths);\n    ngx_queue_init(&qc->free_paths);\n\n    ngx_queue_init(&qc->client_ids);\n    ngx_queue_init(&qc->free_client_ids);\n\n    qc->tp.original_dcid.len = pkt->odcid.len;\n    qc->tp.original_dcid.data = ngx_pstrdup(c->pool, &pkt->odcid);\n    if (qc->tp.original_dcid.data == NULL) {\n         return NGX_ERROR;\n    }\n\n    /* socket to use for further processing */\n    qsock = ngx_quic_alloc_socket(c, qc);\n    if (qsock == NULL) {\n        return NGX_ERROR;\n    }\n\n    /* socket is listening at new server id (autogenerated) */\n    if (ngx_quic_listen(c, qc, qsock) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    qc->tp.initial_scid.len = qsock->sid.len;\n    qc->tp.initial_scid.data = ngx_pnalloc(c->pool, qsock->sid.len);\n    if (qc->tp.initial_scid.data == NULL) {\n        goto failed;\n    }\n    ngx_memcpy(qc->tp.initial_scid.data, qsock->sid.id, qsock->sid.len);\n\n    /* for all packets except first, this is set at udp layer */\n    c->udp = &qsock->udp;\n\n    /* ngx_quic_get_connection(c) macro is now usable */\n\n    /* we have a client identified by scid */\n    cid = ngx_quic_create_client_id(c, &pkt->scid, 0, NULL);\n    if (cid == NULL) {\n        goto failed;\n    }\n\n    /* the client arrived from this path */\n    path = ngx_quic_add_path(c, c->sockaddr, c->socklen);\n    if (path == NULL) {\n        goto failed;\n    }\n\n    if (pkt->validated) {\n        path->state = NGX_QUIC_PATH_VALIDATED;\n        path->validated_at = ngx_time();\n    }\n\n    /* now bind socket to client and path */\n    ngx_quic_connect(c, qsock, path, cid);\n\n    if (ngx_quic_create_temp_socket(c, qc, &pkt->odcid, path, cid) != NGX_OK) {\n        goto failed;\n    }\n\n    /* use this socket as default destination */\n    qc->socket = qsock;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic active socket is #%uL:%uL:%uL (%s)\",\n                   qsock->sid.seqnum, qsock->cid->seqnum, qsock->path->seqnum,\n                   ngx_quic_path_state_str(qsock->path));\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);\n    c->udp = NULL;\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_quic_create_temp_socket(ngx_connection_t *c, ngx_quic_connection_t *qc,\n    ngx_str_t *dcid, ngx_quic_path_t *path, ngx_quic_client_id_t *cid)\n{\n    ngx_str_t              id;\n    ngx_quic_socket_t     *qsock;\n    ngx_quic_server_id_t  *sid;\n\n    qsock = ngx_quic_alloc_socket(c, qc);\n    if (qsock == NULL) {\n        return NGX_ERROR;\n    }\n\n    sid = &qsock->sid;\n\n    sid->seqnum = NGX_QUIC_UNSET_PN; /* mark socket as temporary */\n\n    sid->len = dcid->len;\n    ngx_memcpy(sid->id, dcid->data, dcid->len);\n\n    id.len = sid->len;\n    id.data = sid->id;\n\n    ngx_insert_udp_connection(c, &qsock->udp, &id);\n\n    ngx_queue_insert_tail(&qc->sockets, &qsock->queue);\n\n    qc->nsockets++;\n    qsock->quic = qc;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic socket #%L listening at sid:%xV nsock:%ui\",\n                   (int64_t) sid->seqnum, &id, qc->nsockets);\n\n    ngx_quic_connect(c, qsock, path, cid);\n\n    return NGX_OK;\n}\n\n\nngx_quic_socket_t *\nngx_quic_alloc_socket(ngx_connection_t *c, ngx_quic_connection_t *qc)\n{\n    ngx_queue_t        *q;\n    ngx_quic_socket_t  *sock;\n\n    if (!ngx_queue_empty(&qc->free_sockets)) {\n\n        q = ngx_queue_head(&qc->free_sockets);\n        sock = ngx_queue_data(q, ngx_quic_socket_t, queue);\n\n        ngx_queue_remove(&sock->queue);\n\n        ngx_memzero(sock, sizeof(ngx_quic_socket_t));\n\n    } else {\n\n        sock = ngx_pcalloc(c->pool, sizeof(ngx_quic_socket_t));\n        if (sock == NULL) {\n            return NULL;\n        }\n    }\n\n    return sock;\n}\n\n\nvoid\nngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    ngx_queue_remove(&qsock->queue);\n    ngx_queue_insert_head(&qc->free_sockets, &qsock->queue);\n\n    ngx_rbtree_delete(&c->listening->rbtree, &qsock->udp.node);\n    qc->nsockets--;\n\n    if (qsock->path) {\n        ngx_quic_unref_path(c, qsock->path);\n    }\n\n    if (qsock->cid) {\n        ngx_quic_unref_client_id(c, qsock->cid);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic socket #%L closed nsock:%ui\",\n                   (int64_t) qsock->sid.seqnum, qc->nsockets);\n}\n\n\nvoid\nngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path)\n{\n    ngx_quic_connection_t  *qc;\n\n    path->refcnt--;\n\n    if (path->refcnt) {\n        return;\n    }\n\n    qc = ngx_quic_get_connection(c);\n\n    ngx_queue_remove(&path->queue);\n    ngx_queue_insert_head(&qc->free_paths, &path->queue);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic path #%uL addr:%V removed\",\n                   path->seqnum, &path->addr_text);\n}\n\n\nngx_int_t\nngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,\n    ngx_quic_socket_t *qsock)\n{\n    ngx_str_t              id;\n    ngx_quic_server_id_t  *sid;\n\n    sid = &qsock->sid;\n\n    sid->len = NGX_QUIC_SERVER_CID_LEN;\n\n    if (ngx_quic_create_server_id(c, sid->id) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    sid->seqnum = qc->server_seqnum++;\n\n    id.data = sid->id;\n    id.len = sid->len;\n\n    ngx_insert_udp_connection(c, &qsock->udp, &id);\n\n    ngx_queue_insert_tail(&qc->sockets, &qsock->queue);\n\n    qc->nsockets++;\n    qsock->quic = qc;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic socket #%uL listening at sid:%xV nsock:%ui\",\n                   sid->seqnum, &id, qc->nsockets);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *sock,\n    ngx_quic_path_t *path, ngx_quic_client_id_t *cid)\n{\n    sock->path = path;\n    path->refcnt++;\n\n    sock->cid = cid;\n    cid->refcnt++;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic socket #%L connected to cid #%uL path:%uL\",\n                   (int64_t) sock->sid.seqnum,\n                   sock->cid->seqnum, path->seqnum);\n}\n\n\nvoid\nngx_quic_close_sockets(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    while (!ngx_queue_empty(&qc->sockets)) {\n        q = ngx_queue_head(&qc->sockets);\n        qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);\n\n        ngx_quic_close_socket(c, qsock);\n    }\n}\n\n\nngx_quic_socket_t *\nngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum)\n{\n    ngx_queue_t            *q;\n    ngx_quic_socket_t      *qsock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    for (q = ngx_queue_head(&qc->sockets);\n         q != ngx_queue_sentinel(&qc->sockets);\n         q = ngx_queue_next(q))\n    {\n        qsock = ngx_queue_data(q, ngx_quic_socket_t, queue);\n\n        if (qsock->sid.seqnum == seqnum) {\n            return qsock;\n        }\n    }\n\n    return NULL;\n}\n\n\nngx_quic_socket_t *\nngx_quic_get_unconnected_socket(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    ngx_quic_socket_t      *sock;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    for (q = ngx_queue_head(&qc->sockets);\n         q != ngx_queue_sentinel(&qc->sockets);\n         q = ngx_queue_next(q))\n    {\n        sock = ngx_queue_data(q, ngx_quic_socket_t, queue);\n\n        if (sock->cid == NULL) {\n            return sock;\n        }\n    }\n\n    return NULL;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_socket.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_SOCKET_H_INCLUDED_\n#define _NGX_EVENT_QUIC_SOCKET_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_quic_open_sockets(ngx_connection_t *c,\n    ngx_quic_connection_t *qc, ngx_quic_header_t *pkt);\nvoid ngx_quic_close_sockets(ngx_connection_t *c);\n\nngx_quic_socket_t *ngx_quic_alloc_socket(ngx_connection_t *c,\n    ngx_quic_connection_t *qc);\nngx_int_t ngx_quic_listen(ngx_connection_t *c, ngx_quic_connection_t *qc,\n    ngx_quic_socket_t *qsock);\nvoid ngx_quic_close_socket(ngx_connection_t *c, ngx_quic_socket_t *qsock);\n\nvoid ngx_quic_unref_path(ngx_connection_t *c, ngx_quic_path_t *path);\nvoid ngx_quic_connect(ngx_connection_t *c, ngx_quic_socket_t *qsock,\n    ngx_quic_path_t *path, ngx_quic_client_id_t *cid);\n\nngx_quic_socket_t *ngx_quic_find_socket(ngx_connection_t *c, uint64_t seqnum);\nngx_quic_socket_t *ngx_quic_get_unconnected_socket(ngx_connection_t *c);\n\n\n#endif /* _NGX_EVENT_QUIC_SOCKET_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_ssl.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#include <ngx_event_quic_connection.h>\n\n\n/*\n * RFC 9000, 7.5.  Cryptographic Message Buffering\n *\n * Implementations MUST support buffering at least 4096 bytes of data\n */\n#define NGX_QUIC_MAX_BUFFERED    65535\n\n\n#if BORINGSSL_API_VERSION >= 10\nstatic int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,\n    const uint8_t *secret, size_t secret_len);\nstatic int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,\n    const uint8_t *secret, size_t secret_len);\n#else\nstatic int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const uint8_t *read_secret,\n    const uint8_t *write_secret, size_t secret_len);\n#endif\n\nstatic int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const uint8_t *data, size_t len);\nstatic int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);\nstatic int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, uint8_t alert);\nstatic ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data);\n\n\nstatic SSL_QUIC_METHOD quic_method = {\n#if BORINGSSL_API_VERSION >= 10\n    ngx_quic_set_read_secret,\n    ngx_quic_set_write_secret,\n#else\n    ngx_quic_set_encryption_secrets,\n#endif\n    ngx_quic_add_handshake_data,\n    ngx_quic_flush_flight,\n    ngx_quic_send_alert,\n};\n\n\n#if BORINGSSL_API_VERSION >= 10\n\nstatic int\nngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,\n    const uint8_t *rsecret, size_t secret_len)\n{\n    ngx_connection_t       *c;\n    ngx_quic_connection_t  *qc;\n\n    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n    qc = ngx_quic_get_connection(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_set_read_secret() level:%d\", level);\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic read secret len:%uz %*xs\", secret_len,\n                   secret_len, rsecret);\n#endif\n\n    if (ngx_quic_keys_set_encryption_secret(c->pool, 0, qc->keys, level,\n                                            cipher, rsecret, secret_len)\n        != 1)\n    {\n        return 0;\n    }\n\n    if (level == ssl_encryption_early_data) {\n        if (ngx_quic_init_streams(c) != NGX_OK) {\n            return 0;\n        }\n    }\n\n    return 1;\n}\n\n\nstatic int\nngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,\n    const uint8_t *wsecret, size_t secret_len)\n{\n    ngx_connection_t       *c;\n    ngx_quic_connection_t  *qc;\n\n    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n    qc = ngx_quic_get_connection(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_set_write_secret() level:%d\", level);\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic write secret len:%uz %*xs\", secret_len,\n                   secret_len, wsecret);\n#endif\n\n    return ngx_quic_keys_set_encryption_secret(c->pool, 1, qc->keys, level,\n                                               cipher, wsecret, secret_len);\n}\n\n#else\n\nstatic int\nngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const uint8_t *rsecret,\n    const uint8_t *wsecret, size_t secret_len)\n{\n    ngx_connection_t       *c;\n    const SSL_CIPHER       *cipher;\n    ngx_quic_connection_t  *qc;\n\n    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n    qc = ngx_quic_get_connection(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_set_encryption_secrets() level:%d\", level);\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic read secret len:%uz %*xs\", secret_len,\n                   secret_len, rsecret);\n#endif\n\n    cipher = SSL_get_current_cipher(ssl_conn);\n\n    if (ngx_quic_keys_set_encryption_secret(c->pool, 0, qc->keys, level,\n                                            cipher, rsecret, secret_len)\n        != 1)\n    {\n        return 0;\n    }\n\n    if (level == ssl_encryption_early_data) {\n        if (ngx_quic_init_streams(c) != NGX_OK) {\n            return 0;\n        }\n\n        return 1;\n    }\n\n#ifdef NGX_QUIC_DEBUG_CRYPTO\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic write secret len:%uz %*xs\", secret_len,\n                   secret_len, wsecret);\n#endif\n\n    return ngx_quic_keys_set_encryption_secret(c->pool, 1, qc->keys, level,\n                                               cipher, wsecret, secret_len);\n}\n\n#endif\n\n\nstatic int\nngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,\n    enum ssl_encryption_level_t level, const uint8_t *data, size_t len)\n{\n    u_char                 *p, *end;\n    size_t                  client_params_len;\n    const uint8_t          *client_params;\n    ngx_quic_tp_t           ctp;\n    ngx_quic_frame_t       *frame;\n    ngx_connection_t       *c;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_connection_t  *qc;\n#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)\n    unsigned int            alpn_len;\n    const unsigned char    *alpn_data;\n#endif\n\n    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n    qc = ngx_quic_get_connection(c);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_add_handshake_data\");\n\n    if (!qc->client_tp_done) {\n        /*\n         * things to do once during handshake: check ALPN and transport\n         * parameters; we want to break handshake if something is wrong\n         * here;\n         */\n\n#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)\n\n         SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);\n\n         if (alpn_len == 0) {\n             qc->error = 0x100 + SSL_AD_NO_APPLICATION_PROTOCOL;\n             qc->error_reason = \"unsupported protocol in ALPN extension\";\n\n             ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                           \"quic unsupported protocol in ALPN extension\");\n             return 0;\n         }\n\n#endif\n\n        SSL_get_peer_quic_transport_params(ssl_conn, &client_params,\n                                           &client_params_len);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic SSL_get_peer_quic_transport_params():\"\n                       \" params_len:%ui\", client_params_len);\n\n        if (client_params_len == 0) {\n            /* RFC 9001, 8.2.  QUIC Transport Parameters Extension */\n            qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);\n            qc->error_reason = \"missing transport parameters\";\n\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"missing transport parameters\");\n            return 0;\n        }\n\n        p = (u_char *) client_params;\n        end = p + client_params_len;\n\n        /* defaults for parameters not sent by client */\n        ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));\n\n        if (ngx_quic_parse_transport_params(p, end, &ctp, c->log)\n            != NGX_OK)\n        {\n            qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;\n            qc->error_reason = \"failed to process transport parameters\";\n\n            return 0;\n        }\n\n        if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {\n            return 0;\n        }\n\n        qc->client_tp_done = 1;\n    }\n\n    ctx = ngx_quic_get_send_ctx(qc, level);\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return 0;\n    }\n\n    frame->data = ngx_quic_copy_buf(c, (u_char *) data, len);\n    if (frame->data == NGX_CHAIN_ERROR) {\n        return 0;\n    }\n\n    frame->level = level;\n    frame->type = NGX_QUIC_FT_CRYPTO;\n    frame->u.crypto.offset = ctx->crypto_sent;\n    frame->u.crypto.length = len;\n\n    ctx->crypto_sent += len;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    return 1;\n}\n\n\nstatic int\nngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)\n{\n#if (NGX_DEBUG)\n    ngx_connection_t  *c;\n\n    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_flush_flight()\");\n#endif\n    return 1;\n}\n\n\nstatic int\nngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,\n    uint8_t alert)\n{\n    ngx_connection_t       *c;\n    ngx_quic_connection_t  *qc;\n\n    c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ngx_quic_send_alert() level:%s alert:%d\",\n                   ngx_quic_level_name(level), (int) alert);\n\n    /* already closed on regular shutdown */\n\n    qc = ngx_quic_get_connection(c);\n    if (qc == NULL) {\n        return 1;\n    }\n\n    qc->error = NGX_QUIC_ERR_CRYPTO(alert);\n\n    return 1;\n}\n\n\nngx_int_t\nngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,\n    ngx_quic_frame_t *frame)\n{\n    size_t                    len;\n    uint64_t                  last;\n    ngx_buf_t                *b;\n    ngx_chain_t              *cl, **ll;\n    ngx_quic_send_ctx_t      *ctx;\n    ngx_quic_connection_t    *qc;\n    ngx_quic_crypto_frame_t  *f;\n\n    qc = ngx_quic_get_connection(c);\n    ctx = ngx_quic_get_send_ctx(qc, pkt->level);\n    f = &frame->u.crypto;\n\n    /* no overflow since both values are 62-bit */\n    last = f->offset + f->length;\n\n    if (last > ctx->crypto_received + NGX_QUIC_MAX_BUFFERED) {\n        qc->error = NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED;\n        return NGX_ERROR;\n    }\n\n    if (last <= ctx->crypto_received) {\n        if (pkt->level == ssl_encryption_initial) {\n            /* speeding up handshake completion */\n\n            if (!ngx_queue_empty(&ctx->sent)) {\n                ngx_quic_resend_frames(c, ctx);\n\n                ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);\n                while (!ngx_queue_empty(&ctx->sent)) {\n                    ngx_quic_resend_frames(c, ctx);\n                }\n            }\n        }\n\n        return NGX_OK;\n    }\n\n    if (f->offset > ctx->crypto_received) {\n        return ngx_quic_order_bufs(c, &ctx->crypto, frame->data,\n                                   f->offset - ctx->crypto_received);\n    }\n\n    ngx_quic_trim_bufs(frame->data, ctx->crypto_received - f->offset);\n\n    if (ngx_quic_crypto_input(c, frame->data) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_quic_trim_bufs(ctx->crypto, last - ctx->crypto_received);\n    ctx->crypto_received = last;\n\n    cl = ctx->crypto;\n    ll = &cl;\n    len = 0;\n\n    while (*ll) {\n        b = (*ll)->buf;\n\n        if (b->sync && b->pos != b->last) {\n            /* hole */\n            break;\n        }\n\n        len += b->last - b->pos;\n        ll = &(*ll)->next;\n    }\n\n    ctx->crypto_received += len;\n    ctx->crypto = *ll;\n    *ll = NULL;\n\n    if (cl) {\n        if (ngx_quic_crypto_input(c, cl) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        ngx_quic_free_bufs(c, cl);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data)\n{\n    int                     n, sslerr;\n    ngx_buf_t              *b;\n    ngx_chain_t            *cl;\n    ngx_ssl_conn_t         *ssl_conn;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    ssl_conn = c->ssl->connection;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic SSL_quic_read_level:%d SSL_quic_write_level:%d\",\n                   (int) SSL_quic_read_level(ssl_conn),\n                   (int) SSL_quic_write_level(ssl_conn));\n\n    for (cl = data; cl; cl = cl->next) {\n        b = cl->buf;\n\n        if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn),\n                                   b->pos, b->last - b->pos))\n        {\n            ngx_ssl_error(NGX_LOG_INFO, c->log, 0,\n                          \"SSL_provide_quic_data() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    n = SSL_do_handshake(ssl_conn);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic SSL_quic_read_level:%d SSL_quic_write_level:%d\",\n                   (int) SSL_quic_read_level(ssl_conn),\n                   (int) SSL_quic_write_level(ssl_conn));\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_do_handshake: %d\", n);\n\n    if (n <= 0) {\n        sslerr = SSL_get_error(ssl_conn, n);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_get_error: %d\",\n                       sslerr);\n\n        if (sslerr != SSL_ERROR_WANT_READ) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0, \"SSL_do_handshake() failed\");\n            qc->error_reason = \"handshake failed\";\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    if (SSL_in_init(ssl_conn)) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic ssl cipher:%s\", SSL_get_cipher(ssl_conn));\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic handshake completed successfully\");\n\n    c->ssl->handshaked = 1;\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;\n    ngx_quic_queue_frame(qc, frame);\n\n    if (qc->conf->retry) {\n        if (ngx_quic_send_new_token(c, qc->socket->path) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    /*\n     * RFC 9001, 9.5.  Header Protection Timing Side Channels\n     *\n     * Generating next keys before a key update is received.\n     */\n\n    if (ngx_quic_keys_update(c, qc->keys) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /*\n     * RFC 9001, 4.9.2.  Discarding Handshake Keys\n     *\n     * An endpoint MUST discard its Handshake keys\n     * when the TLS handshake is confirmed.\n     */\n    ngx_quic_discard_ctx(c, ssl_encryption_handshake);\n\n    /* start accepting clients on negotiated number of server ids */\n    if (ngx_quic_create_sockets(c) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_quic_init_streams(c) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_init_connection(ngx_connection_t *c)\n{\n    u_char                 *p;\n    size_t                  clen;\n    ssize_t                 len;\n    ngx_str_t               dcid;\n    ngx_ssl_conn_t         *ssl_conn;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (ngx_ssl_create_connection(qc->conf->ssl, c, NGX_SSL_BUFFER) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    c->ssl->no_wait_shutdown = 1;\n\n    ssl_conn = c->ssl->connection;\n\n    if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic SSL_set_quic_method() failed\");\n        return NGX_ERROR;\n    }\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n    if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {\n        SSL_set_quic_early_data_enabled(ssl_conn, 1);\n    }\n#endif\n\n#if BORINGSSL_API_VERSION >= 13\n    SSL_set_quic_use_legacy_codepoint(ssl_conn, qc->version != 1);\n#endif\n\n    dcid.data = qc->socket->sid.id;\n    dcid.len = qc->socket->sid.len;\n\n    if (ngx_quic_new_sr_token(c, &dcid, qc->conf->sr_token_key, qc->tp.sr_token)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen);\n    /* always succeeds */\n\n    p = ngx_pnalloc(c->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    len = ngx_quic_create_transport_params(p, p + len, &qc->tp, NULL);\n    if (len < 0) {\n        return NGX_ERROR;\n    }\n\n#ifdef NGX_QUIC_DEBUG_PACKETS\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic transport parameters len:%uz %*xs\", len, len, p);\n#endif\n\n    if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic SSL_set_quic_transport_params() failed\");\n        return NGX_ERROR;\n    }\n\n#if BORINGSSL_API_VERSION >= 11\n    if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"quic SSL_set_quic_early_data_context() failed\");\n        return NGX_ERROR;\n    }\n#endif\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_ssl.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_SSL_H_INCLUDED_\n#define _NGX_EVENT_QUIC_SSL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\nngx_int_t ngx_quic_init_connection(ngx_connection_t *c);\n\nngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);\n\n#endif /* _NGX_EVENT_QUIC_SSL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_streams.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#include <ngx_event_quic_connection.h>\n\n\n#define NGX_QUIC_STREAM_GONE     (void *) -1\n\n\nstatic ngx_quic_stream_t *ngx_quic_create_client_stream(ngx_connection_t *c,\n    uint64_t id);\nstatic ngx_int_t ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id);\nstatic ngx_int_t ngx_quic_init_stream(ngx_quic_stream_t *qs);\nstatic void ngx_quic_init_streams_handler(ngx_connection_t *c);\nstatic ngx_quic_stream_t *ngx_quic_create_stream(ngx_connection_t *c,\n    uint64_t id);\nstatic void ngx_quic_empty_handler(ngx_event_t *ev);\nstatic ssize_t ngx_quic_stream_recv(ngx_connection_t *c, u_char *buf,\n    size_t size);\nstatic ssize_t ngx_quic_stream_send(ngx_connection_t *c, u_char *buf,\n    size_t size);\nstatic ngx_chain_t *ngx_quic_stream_send_chain(ngx_connection_t *c,\n    ngx_chain_t *in, off_t limit);\nstatic size_t ngx_quic_max_stream_flow(ngx_connection_t *c);\nstatic void ngx_quic_stream_cleanup_handler(void *data);\nstatic ngx_int_t ngx_quic_control_flow(ngx_connection_t *c, uint64_t last);\nstatic ngx_int_t ngx_quic_update_flow(ngx_connection_t *c, uint64_t last);\nstatic ngx_int_t ngx_quic_update_max_stream_data(ngx_connection_t *c);\nstatic ngx_int_t ngx_quic_update_max_data(ngx_connection_t *c);\n\n\nngx_connection_t *\nngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi)\n{\n    uint64_t                id;\n    ngx_quic_stream_t      *qs, *nqs;\n    ngx_quic_connection_t  *qc;\n\n    qs = c->quic;\n    qc = ngx_quic_get_connection(qs->parent);\n\n    if (bidi) {\n        if (qc->streams.server_streams_bidi\n            >= qc->streams.server_max_streams_bidi)\n        {\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic too many server bidi streams:%uL\",\n                           qc->streams.server_streams_bidi);\n            return NULL;\n        }\n\n        id = (qc->streams.server_streams_bidi << 2)\n             | NGX_QUIC_STREAM_SERVER_INITIATED;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic creating server bidi stream\"\n                       \" streams:%uL max:%uL id:0x%xL\",\n                       qc->streams.server_streams_bidi,\n                       qc->streams.server_max_streams_bidi, id);\n\n        qc->streams.server_streams_bidi++;\n\n    } else {\n        if (qc->streams.server_streams_uni\n            >= qc->streams.server_max_streams_uni)\n        {\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic too many server uni streams:%uL\",\n                           qc->streams.server_streams_uni);\n            return NULL;\n        }\n\n        id = (qc->streams.server_streams_uni << 2)\n             | NGX_QUIC_STREAM_SERVER_INITIATED\n             | NGX_QUIC_STREAM_UNIDIRECTIONAL;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic creating server uni stream\"\n                       \" streams:%uL max:%uL id:0x%xL\",\n                       qc->streams.server_streams_uni,\n                       qc->streams.server_max_streams_uni, id);\n\n        qc->streams.server_streams_uni++;\n    }\n\n    nqs = ngx_quic_create_stream(qs->parent, id);\n    if (nqs == NULL) {\n        return NULL;\n    }\n\n    return nqs->connection;\n}\n\n\nvoid\nngx_quic_rbtree_insert_stream(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_quic_stream_t   *qn, *qnt;\n\n    for ( ;; ) {\n        qn = (ngx_quic_stream_t *) node;\n        qnt = (ngx_quic_stream_t *) temp;\n\n        p = (qn->id < qnt->id) ? &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\nngx_quic_stream_t *\nngx_quic_find_stream(ngx_rbtree_t *rbtree, uint64_t id)\n{\n    ngx_rbtree_node_t  *node, *sentinel;\n    ngx_quic_stream_t  *qn;\n\n    node = rbtree->root;\n    sentinel = rbtree->sentinel;\n\n    while (node != sentinel) {\n        qn = (ngx_quic_stream_t *) node;\n\n        if (id == qn->id) {\n            return qn;\n        }\n\n        node = (id < qn->id) ? node->left : node->right;\n    }\n\n    return NULL;\n}\n\n\nngx_int_t\nngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)\n{\n    ngx_pool_t         *pool;\n    ngx_queue_t        *q;\n    ngx_event_t        *rev, *wev;\n    ngx_rbtree_t       *tree;\n    ngx_rbtree_node_t  *node;\n    ngx_quic_stream_t  *qs;\n\n#if (NGX_DEBUG)\n    ngx_uint_t          ns;\n#endif\n\n    while (!ngx_queue_empty(&qc->streams.uninitialized)) {\n        q = ngx_queue_head(&qc->streams.uninitialized);\n        ngx_queue_remove(q);\n\n        qs = ngx_queue_data(q, ngx_quic_stream_t, queue);\n        pool = qs->connection->pool;\n\n        ngx_close_connection(qs->connection);\n        ngx_destroy_pool(pool);\n    }\n\n    tree = &qc->streams.tree;\n\n    if (tree->root == tree->sentinel) {\n        return NGX_OK;\n    }\n\n#if (NGX_DEBUG)\n    ns = 0;\n#endif\n\n    for (node = ngx_rbtree_min(tree->root, tree->sentinel);\n         node;\n         node = ngx_rbtree_next(tree, node))\n    {\n        qs = (ngx_quic_stream_t *) node;\n\n        rev = qs->connection->read;\n        rev->error = 1;\n        rev->ready = 1;\n\n        wev = qs->connection->write;\n        wev->error = 1;\n        wev->ready = 1;\n\n        ngx_post_event(rev, &ngx_posted_events);\n\n        if (rev->timer_set) {\n            ngx_del_timer(rev);\n        }\n\n#if (NGX_DEBUG)\n        ns++;\n#endif\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic connection has %ui active streams\", ns);\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_quic_reset_stream(ngx_connection_t *c, ngx_uint_t err)\n{\n    ngx_event_t            *wev;\n    ngx_connection_t       *pc;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    wev = c->write;\n\n    if (wev->error) {\n        return NGX_OK;\n    }\n\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n\n    frame = ngx_quic_alloc_frame(pc);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_RESET_STREAM;\n    frame->u.reset_stream.id = qs->id;\n    frame->u.reset_stream.error_code = err;\n    frame->u.reset_stream.final_size = c->sent;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    wev->error = 1;\n    wev->ready = 1;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_shutdown_stream(ngx_connection_t *c, int how)\n{\n    ngx_event_t            *wev;\n    ngx_connection_t       *pc;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    if (how != NGX_WRITE_SHUTDOWN) {\n        return NGX_OK;\n    }\n\n    wev = c->write;\n\n    if (wev->error) {\n        return NGX_OK;\n    }\n\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n\n    frame = ngx_quic_alloc_frame(pc);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL shutdown\", qs->id);\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_STREAM;\n    frame->u.stream.off = 1;\n    frame->u.stream.len = 1;\n    frame->u.stream.fin = 1;\n\n    frame->u.stream.stream_id = qs->id;\n    frame->u.stream.offset = c->sent;\n    frame->u.stream.length = 0;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    wev->ready = 1;\n    wev->error = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_quic_stream_t *\nngx_quic_create_client_stream(ngx_connection_t *c, uint64_t id)\n{\n    uint64_t                min_id;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL is new\", id);\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc->shutdown || qc->closing) {\n        return NGX_QUIC_STREAM_GONE;\n    }\n\n    if (id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {\n\n        if (id & NGX_QUIC_STREAM_SERVER_INITIATED) {\n            if ((id >> 2) < qc->streams.server_streams_uni) {\n                return NGX_QUIC_STREAM_GONE;\n            }\n\n            qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR;\n            return NULL;\n        }\n\n        if ((id >> 2) < qc->streams.client_streams_uni) {\n            return NGX_QUIC_STREAM_GONE;\n        }\n\n        if ((id >> 2) >= qc->streams.client_max_streams_uni) {\n            qc->error = NGX_QUIC_ERR_STREAM_LIMIT_ERROR;\n            return NULL;\n        }\n\n        min_id = (qc->streams.client_streams_uni << 2)\n                 | NGX_QUIC_STREAM_UNIDIRECTIONAL;\n        qc->streams.client_streams_uni = (id >> 2) + 1;\n\n    } else {\n\n        if (id & NGX_QUIC_STREAM_SERVER_INITIATED) {\n            if ((id >> 2) < qc->streams.server_streams_bidi) {\n                return NGX_QUIC_STREAM_GONE;\n            }\n\n            qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR;\n            return NULL;\n        }\n\n        if ((id >> 2) < qc->streams.client_streams_bidi) {\n            return NGX_QUIC_STREAM_GONE;\n        }\n\n        if ((id >> 2) >= qc->streams.client_max_streams_bidi) {\n            qc->error = NGX_QUIC_ERR_STREAM_LIMIT_ERROR;\n            return NULL;\n        }\n\n        min_id = (qc->streams.client_streams_bidi << 2);\n        qc->streams.client_streams_bidi = (id >> 2) + 1;\n    }\n\n    /*\n     * RFC 9000, 2.1.  Stream Types and Identifiers\n     *\n     * successive streams of each type are created with numerically increasing\n     * stream IDs.  A stream ID that is used out of order results in all\n     * streams of that type with lower-numbered stream IDs also being opened.\n     */\n\n    for ( /* void */ ; min_id < id; min_id += 0x04) {\n\n        qs = ngx_quic_create_stream(c, min_id);\n\n        if (qs == NULL) {\n            if (ngx_quic_reject_stream(c, min_id) != NGX_OK) {\n                return NULL;\n            }\n\n            continue;\n        }\n\n        if (ngx_quic_init_stream(qs) != NGX_OK) {\n            return NULL;\n        }\n\n        if (qc->shutdown || qc->closing) {\n            return NGX_QUIC_STREAM_GONE;\n        }\n    }\n\n    qs = ngx_quic_create_stream(c, id);\n\n    if (qs == NULL) {\n        if (ngx_quic_reject_stream(c, id) != NGX_OK) {\n            return NULL;\n        }\n\n        return NGX_QUIC_STREAM_GONE;\n    }\n\n    return qs;\n}\n\n\nstatic ngx_int_t\nngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)\n{\n    uint64_t                code;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    code = (id & NGX_QUIC_STREAM_UNIDIRECTIONAL)\n           ? qc->conf->stream_reject_code_uni\n           : qc->conf->stream_reject_code_bidi;\n\n    if (code == 0) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL reject err:0x%xL\", id, code);\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_RESET_STREAM;\n    frame->u.reset_stream.id = id;\n    frame->u.reset_stream.error_code = code;\n    frame->u.reset_stream.final_size = 0;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_STOP_SENDING;\n    frame->u.stop_sending.id = id;\n    frame->u.stop_sending.error_code = code;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_init_stream(ngx_quic_stream_t *qs)\n{\n    ngx_connection_t       *c;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(qs->parent);\n\n    c = qs->connection;\n\n    if (!qc->streams.initialized) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic postpone stream init\");\n\n        ngx_queue_insert_tail(&qc->streams.uninitialized, &qs->queue);\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic init stream\");\n\n    c->listening->handler(c);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_init_streams(ngx_connection_t *c)\n{\n    ngx_int_t               rc;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (qc->streams.initialized) {\n        return NGX_OK;\n    }\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->ssl->handler = ngx_quic_init_streams_handler;\n        return NGX_OK;\n    }\n\n    ngx_quic_init_streams_handler(c);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_quic_init_streams_handler(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"quic init streams\");\n\n    qc = ngx_quic_get_connection(c);\n\n    while (!ngx_queue_empty(&qc->streams.uninitialized)) {\n        q = ngx_queue_head(&qc->streams.uninitialized);\n        ngx_queue_remove(q);\n\n        qs = ngx_queue_data(q, ngx_quic_stream_t, queue);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, qs->connection->log, 0,\n                       \"quic init postponed stream\");\n\n        qs->connection->listening->handler(qs->connection);\n    }\n\n    qc->streams.initialized = 1;\n}\n\n\nstatic ngx_quic_stream_t *\nngx_quic_create_stream(ngx_connection_t *c, uint64_t id)\n{\n    ngx_log_t              *log;\n    ngx_pool_t             *pool;\n    ngx_connection_t       *sc;\n    ngx_quic_stream_t      *qs;\n    ngx_pool_cleanup_t     *cln;\n    ngx_quic_connection_t  *qc;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL create\", id);\n\n    qc = ngx_quic_get_connection(c);\n\n    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, c->log);\n    if (pool == NULL) {\n        return NULL;\n    }\n\n    qs = ngx_pcalloc(pool, sizeof(ngx_quic_stream_t));\n    if (qs == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    qs->node.key = id;\n    qs->parent = c;\n    qs->id = id;\n    qs->final_size = (uint64_t) -1;\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    *log = *c->log;\n    pool->log = log;\n\n    sc = ngx_get_connection(c->fd, log);\n    if (sc == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    qs->connection = sc;\n\n    sc->quic = qs;\n    sc->shared = 1;\n    sc->type = SOCK_STREAM;\n    sc->pool = pool;\n    sc->ssl = c->ssl;\n    sc->sockaddr = c->sockaddr;\n    sc->listening = c->listening;\n    sc->addr_text = c->addr_text;\n    sc->local_sockaddr = c->local_sockaddr;\n    sc->local_socklen = c->local_socklen;\n    sc->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n    sc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n\n    sc->recv = ngx_quic_stream_recv;\n    sc->send = ngx_quic_stream_send;\n    sc->send_chain = ngx_quic_stream_send_chain;\n\n    sc->read->log = log;\n    sc->write->log = log;\n\n    sc->read->handler = ngx_quic_empty_handler;\n    sc->write->handler = ngx_quic_empty_handler;\n\n    log->connection = sc->number;\n\n    if ((id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0\n        || (id & NGX_QUIC_STREAM_SERVER_INITIATED))\n    {\n        sc->write->ready = 1;\n    }\n\n    if (id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {\n        if (id & NGX_QUIC_STREAM_SERVER_INITIATED) {\n            qs->send_max_data = qc->ctp.initial_max_stream_data_uni;\n\n        } else {\n            qs->recv_max_data = qc->tp.initial_max_stream_data_uni;\n        }\n\n    } else {\n        if (id & NGX_QUIC_STREAM_SERVER_INITIATED) {\n            qs->send_max_data = qc->ctp.initial_max_stream_data_bidi_remote;\n            qs->recv_max_data = qc->tp.initial_max_stream_data_bidi_local;\n\n        } else {\n            qs->send_max_data = qc->ctp.initial_max_stream_data_bidi_local;\n            qs->recv_max_data = qc->tp.initial_max_stream_data_bidi_remote;\n        }\n    }\n\n    qs->recv_window = qs->recv_max_data;\n\n    cln = ngx_pool_cleanup_add(pool, 0);\n    if (cln == NULL) {\n        ngx_close_connection(sc);\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    cln->handler = ngx_quic_stream_cleanup_handler;\n    cln->data = sc;\n\n    ngx_rbtree_insert(&qc->streams.tree, &qs->node);\n\n    return qs;\n}\n\n\nstatic void\nngx_quic_empty_handler(ngx_event_t *ev)\n{\n}\n\n\nstatic ssize_t\nngx_quic_stream_recv(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    ssize_t             len, n;\n    ngx_buf_t          *b;\n    ngx_chain_t        *cl, **ll;\n    ngx_event_t        *rev;\n    ngx_connection_t   *pc;\n    ngx_quic_stream_t  *qs;\n\n    qs = c->quic;\n    pc = qs->parent;\n    rev = c->read;\n\n    if (rev->error) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL recv eof:%d buf:%uz\",\n                   qs->id, rev->pending_eof, size);\n\n    if (qs->in == NULL || qs->in->buf->sync) {\n        rev->ready = 0;\n\n        if (qs->recv_offset == qs->final_size) {\n            rev->eof = 1;\n            return 0;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic stream id:0x%xL recv() not ready\", qs->id);\n        return NGX_AGAIN;\n    }\n\n    len = 0;\n    cl = qs->in;\n\n    for (ll = &cl; *ll; ll = &(*ll)->next) {\n        b = (*ll)->buf;\n\n        if (b->sync) {\n            /* hole */\n            break;\n        }\n\n        n = ngx_min(b->last - b->pos, (ssize_t) size);\n        buf = ngx_cpymem(buf, b->pos, n);\n\n        len += n;\n        size -= n;\n        b->pos += n;\n\n        if (b->pos != b->last) {\n            break;\n        }\n    }\n\n    qs->in = *ll;\n    *ll = NULL;\n\n    ngx_quic_free_bufs(pc, cl);\n\n    if (qs->in == NULL) {\n        rev->ready = rev->pending_eof;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL recv len:%z\", qs->id, len);\n\n    if (ngx_quic_update_flow(c, qs->recv_offset + len) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return len;\n}\n\n\nstatic ssize_t\nngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    ngx_buf_t    b;\n    ngx_chain_t  cl;\n\n    ngx_memzero(&b, sizeof(ngx_buf_t));\n\n    b.memory = 1;\n    b.pos = buf;\n    b.last = buf + size;\n\n    cl.buf = &b;\n    cl.next = NULL;\n\n    if (ngx_quic_stream_send_chain(c, &cl, 0) == NGX_CHAIN_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (b.pos == buf) {\n        return NGX_AGAIN;\n    }\n\n    return b.pos - buf;\n}\n\n\nstatic ngx_chain_t *\nngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    size_t                  n, flow;\n    ngx_event_t            *wev;\n    ngx_chain_t            *cl;\n    ngx_connection_t       *pc;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n    ngx_quic_send_ctx_t    *ctx;\n    ngx_quic_fqueue_t      *fqueue;\n\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n    wev = c->write;\n\n    if (wev->error) {\n        return NGX_CHAIN_ERROR;\n    }\n\n    flow = ngx_quic_max_stream_flow(c);\n    if (flow == 0) {\n        wev->ready = 0;\n        return in;\n    }\n\n    n = (limit && (size_t) limit < flow) ? (size_t) limit : flow;\n\n    if (qc->streams.exemptions) {\n        if (qc->streams.exemptions > n) {\n            qc->streams.exemptions -= n;\n        } else {\n            qc->streams.exemptions = 0;\n        }\n    }\n\n    frame = ngx_quic_alloc_frame(pc);\n    if (frame == NULL) {\n        return NGX_CHAIN_ERROR;\n    }\n\n    frame->data = ngx_quic_copy_chain(pc, in, n);\n    if (frame->data == NGX_CHAIN_ERROR) {\n        return NGX_CHAIN_ERROR;\n    }\n\n    for (n = 0, cl = frame->data; cl; cl = cl->next) {\n        n += ngx_buf_size(cl->buf);\n    }\n\n    while (in && ngx_buf_size(in->buf) == 0) {\n        in = in->next;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_STREAM;\n    frame->u.stream.off = 1;\n    frame->u.stream.len = 1;\n    frame->u.stream.fin = 0;\n\n    frame->u.stream.stream_id = qs->id;\n    frame->u.stream.offset = c->sent;\n    frame->u.stream.length = n;\n\n    c->sent += n;\n    qc->streams.sent += n;\n\n    if (qc->conf->stream_shuffle) {\n        ctx = ngx_quic_get_send_ctx(qc, frame->level);\n        fqueue = qs->fqueue;\n\n        if (fqueue == NULL) {\n            fqueue = ngx_pcalloc(pc->pool, sizeof(ngx_quic_fqueue_t));\n            if (fqueue == NULL) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            fqueue->frames = ngx_pcalloc(pc->pool, sizeof(ngx_queue_t));\n            if (fqueue->frames == NULL) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            ngx_queue_init(fqueue->frames);\n            qs->fqueue = fqueue;\n        }\n\n        if (!fqueue->attached) {\n            ngx_queue_insert_tail(&ctx->fqueues, &fqueue->queue);\n            fqueue->attached = 1;\n        }\n\n        ngx_quic_queue_frame_after(qc, frame, ngx_queue_last(fqueue->frames), 1);\n    } else {\n        ngx_quic_queue_frame(qc, frame);\n    }\n\n    wev->ready = (n < flow) ? 1 : 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic send_chain sent:%uz\", n);\n\n\n    if (qc->conf->nodelay) {\n        (void) ngx_quic_output(pc);\n    }\n\n    return in;\n}\n\n\nstatic size_t\nngx_quic_max_stream_flow(ngx_connection_t *c)\n{\n    size_t                  size;\n    uint64_t                sent, unacked;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qs = c->quic;\n    qc = ngx_quic_get_connection(qs->parent);\n\n    size = qc->conf->stream_buf_size + qc->streams.exemptions;\n    sent = c->sent;\n    unacked = sent - qs->acked;\n\n    if (qc->streams.send_max_data == 0) {\n        qc->streams.send_max_data = qc->ctp.initial_max_data;\n    }\n\n    if (unacked >= qc->conf->stream_buf_size) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic send flow hit buffer size\");\n        return 0;\n    }\n\n    if (unacked + size > qc->conf->stream_buf_size) {\n        size = qc->conf->stream_buf_size - unacked;\n    }\n\n    if (qc->streams.sent >= qc->streams.send_max_data) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic send flow hit MAX_DATA\");\n        return 0;\n    }\n\n    if (qc->streams.sent + size > qc->streams.send_max_data) {\n        size = qc->streams.send_max_data - qc->streams.sent;\n    }\n\n    if (sent >= qs->send_max_data) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"quic send flow hit MAX_STREAM_DATA\");\n        return 0;\n    }\n\n    if (sent + size > qs->send_max_data) {\n        size = qs->send_max_data - sent;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic send flow:%uz\", size);\n\n    return size;\n}\n\n\nstatic void\nngx_quic_stream_cleanup_handler(void *data)\n{\n    ngx_connection_t *c = data;\n\n    ngx_connection_t       *pc;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n    ngx_quic_send_ctx_t    *ctx;\n\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL cleanup\", qs->id);\n\n    ngx_rbtree_delete(&qc->streams.tree, &qs->node);\n    ngx_quic_free_bufs(pc, qs->in);\n\n    if (qc->closing) {\n        /* schedule handler call to continue ngx_quic_close_connection() */\n        ngx_post_event(pc->read, &ngx_posted_events);\n        return;\n    }\n\n    if (qc->error) {\n        goto done;\n    }\n\n    c->read->pending_eof = 1;\n\n    (void) ngx_quic_update_flow(c, qs->recv_last);\n\n    if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0\n        || (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0)\n    {\n        if (!c->read->pending_eof && !c->read->error\n            && qc->conf->stream_close_code)\n        {\n            frame = ngx_quic_alloc_frame(pc);\n            if (frame == NULL) {\n                goto done;\n            }\n\n            frame->level = ssl_encryption_application;\n            frame->type = NGX_QUIC_FT_STOP_SENDING;\n            frame->u.stop_sending.id = qs->id;\n            frame->u.stop_sending.error_code = qc->conf->stream_close_code;\n\n            ngx_quic_queue_frame(qc, frame);\n        }\n    }\n\n    if ((qs->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0) {\n        frame = ngx_quic_alloc_frame(pc);\n        if (frame == NULL) {\n            goto done;\n        }\n\n        frame->level = ssl_encryption_application;\n        frame->type = NGX_QUIC_FT_MAX_STREAMS;\n\n        if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {\n            frame->u.max_streams.limit = ++qc->streams.client_max_streams_uni;\n            frame->u.max_streams.bidi = 0;\n\n        } else {\n            frame->u.max_streams.limit = ++qc->streams.client_max_streams_bidi;\n            frame->u.max_streams.bidi = 1;\n        }\n\n        ngx_quic_queue_frame(qc, frame);\n\n        if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {\n            /* do not send fin for client unidirectional streams */\n            goto done;\n        }\n    }\n\n    if (c->write->error) {\n        goto done;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stream id:0x%xL send fin\", qs->id);\n\n    frame = ngx_quic_alloc_frame(pc);\n    if (frame == NULL) {\n        goto done;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_STREAM;\n    frame->u.stream.off = 1;\n    frame->u.stream.len = 1;\n    frame->u.stream.fin = 1;\n\n    frame->u.stream.stream_id = qs->id;\n    frame->u.stream.offset = c->sent;\n    frame->u.stream.length = 0;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);\n    ctx->ack_immediately = 1;\n\ndone:\n\n    (void) ngx_quic_output(pc);\n\n    if (qc->shutdown) {\n        ngx_post_event(pc->read, &ngx_posted_events);\n    }\n}\n\n\nngx_int_t\nngx_quic_handle_stream_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,\n    ngx_quic_frame_t *frame)\n{\n    uint64_t                  last;\n    ngx_pool_t               *pool;\n    ngx_event_t              *rev;\n    ngx_connection_t         *sc;\n    ngx_quic_stream_t        *qs;\n    ngx_quic_connection_t    *qc;\n    ngx_quic_stream_frame_t  *f;\n    ngx_quic_send_ctx_t      *ctx;\n\n    qc = ngx_quic_get_connection(c);\n    f = &frame->u.stream;\n\n    if ((f->stream_id & NGX_QUIC_STREAM_UNIDIRECTIONAL)\n        && (f->stream_id & NGX_QUIC_STREAM_SERVER_INITIATED))\n    {\n        qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR;\n        return NGX_ERROR;\n    }\n\n    /* no overflow since both values are 62-bit */\n    last = f->offset + f->length;\n\n    qs = ngx_quic_find_stream(&qc->streams.tree, f->stream_id);\n\n    if (qs == NULL) {\n        qs = ngx_quic_create_client_stream(c, f->stream_id);\n\n        if (qs == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (qs == NGX_QUIC_STREAM_GONE) {\n            return NGX_OK;\n        }\n\n        sc = qs->connection;\n\n        if (ngx_quic_control_flow(sc, last) != NGX_OK) {\n            goto cleanup;\n        }\n\n        if (f->fin) {\n            sc->read->pending_eof = 1;\n            qs->final_size = last;\n\n            ctx = ngx_quic_get_send_ctx(qc, frame->level);\n            ctx->ack_immediately = 1;\n        }\n\n        if (f->offset == 0) {\n            sc->read->ready = 1;\n        }\n\n        if (ngx_quic_order_bufs(c, &qs->in, frame->data, f->offset) != NGX_OK) {\n            goto cleanup;\n        }\n\n        return ngx_quic_init_stream(qs);\n    }\n\n    sc = qs->connection;\n\n    rev = sc->read;\n\n    if (rev->error) {\n        return NGX_OK;\n    }\n\n    if (ngx_quic_control_flow(sc, last) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (qs->final_size != (uint64_t) -1 && last > qs->final_size) {\n        qc->error = NGX_QUIC_ERR_FINAL_SIZE_ERROR;\n        return NGX_ERROR;\n    }\n\n    if (last <= qs->recv_offset) {\n        return NGX_OK;\n    }\n\n    if (f->offset < qs->recv_offset) {\n        ngx_quic_trim_bufs(frame->data, qs->recv_offset - f->offset);\n        f->offset = qs->recv_offset;\n    }\n\n    if (f->fin) {\n        if (qs->final_size != (uint64_t) -1 && qs->final_size != last) {\n            qc->error = NGX_QUIC_ERR_FINAL_SIZE_ERROR;\n            return NGX_ERROR;\n        }\n\n        if (qs->recv_last > last) {\n            qc->error = NGX_QUIC_ERR_FINAL_SIZE_ERROR;\n            return NGX_ERROR;\n        }\n\n        ctx = ngx_quic_get_send_ctx(qc, frame->level);\n        ctx->ack_immediately = 1;\n\n        rev->pending_eof = 1;\n        qs->final_size = last;\n    }\n\n    if (ngx_quic_order_bufs(c, &qs->in, frame->data,\n                            f->offset - qs->recv_offset)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (f->offset == qs->recv_offset) {\n        rev->ready = 1;\n\n        if (rev->active) {\n            rev->handler(rev);\n        }\n    }\n\n    return NGX_OK;\n\ncleanup:\n\n    pool = sc->pool;\n\n    ngx_close_connection(sc);\n    ngx_destroy_pool(pool);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_quic_handle_max_data_frame(ngx_connection_t *c,\n    ngx_quic_max_data_frame_t *f)\n{\n    ngx_event_t            *wev;\n    ngx_rbtree_t           *tree;\n    ngx_rbtree_node_t      *node;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n    tree = &qc->streams.tree;\n\n    if (f->max_data <= qc->streams.send_max_data) {\n        return NGX_OK;\n    }\n\n    if (tree->root != tree->sentinel\n        && qc->streams.sent >= qc->streams.send_max_data)\n    {\n\n        for (node = ngx_rbtree_min(tree->root, tree->sentinel);\n             node;\n             node = ngx_rbtree_next(tree, node))\n        {\n            qs = (ngx_quic_stream_t *) node;\n            wev = qs->connection->write;\n\n            if (wev->active) {\n                wev->ready = 1;\n                ngx_post_event(wev, &ngx_posted_events);\n            }\n        }\n    }\n\n    qc->streams.send_max_data = f->max_data;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_handle_streams_blocked_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_streams_blocked_frame_t *f)\n{\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_handle_data_blocked_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_data_blocked_frame_t *f)\n{\n    return ngx_quic_update_max_data(c);\n}\n\n\nngx_int_t\nngx_quic_handle_stream_data_blocked_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_stream_data_blocked_frame_t *f)\n{\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL)\n        && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED))\n    {\n        qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR;\n        return NGX_ERROR;\n    }\n\n    qs = ngx_quic_find_stream(&qc->streams.tree, f->id);\n\n    if (qs == NULL) {\n        qs = ngx_quic_create_client_stream(c, f->id);\n\n        if (qs == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (qs == NGX_QUIC_STREAM_GONE) {\n            return NGX_OK;\n        }\n\n        return ngx_quic_init_stream(qs);\n    }\n\n    return ngx_quic_update_max_stream_data(qs->connection);\n}\n\n\nngx_int_t\nngx_quic_handle_max_stream_data_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_max_stream_data_frame_t *f)\n{\n    uint64_t                sent;\n    ngx_event_t            *wev;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL)\n        && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0)\n    {\n        qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR;\n        return NGX_ERROR;\n    }\n\n    qs = ngx_quic_find_stream(&qc->streams.tree, f->id);\n\n    if (qs == NULL) {\n        qs = ngx_quic_create_client_stream(c, f->id);\n\n        if (qs == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (qs == NGX_QUIC_STREAM_GONE) {\n            return NGX_OK;\n        }\n\n        if (f->limit > qs->send_max_data) {\n            qs->send_max_data = f->limit;\n        }\n\n        return ngx_quic_init_stream(qs);\n    }\n\n    if (f->limit <= qs->send_max_data) {\n        return NGX_OK;\n    }\n\n    sent = qs->connection->sent;\n\n    if (sent >= qs->send_max_data) {\n        wev = qs->connection->write;\n\n        if (wev->active) {\n            wev->ready = 1;\n            ngx_post_event(wev, &ngx_posted_events);\n        }\n    }\n\n    qs->send_max_data = f->limit;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_handle_reset_stream_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_reset_stream_frame_t *f)\n{\n    ngx_pool_t             *pool;\n    ngx_event_t            *rev;\n    ngx_connection_t       *sc;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL)\n        && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED))\n    {\n        qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR;\n        return NGX_ERROR;\n    }\n\n    qs = ngx_quic_find_stream(&qc->streams.tree, f->id);\n\n    if (qs == NULL) {\n        qs = ngx_quic_create_client_stream(c, f->id);\n\n        if (qs == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (qs == NGX_QUIC_STREAM_GONE) {\n            return NGX_OK;\n        }\n\n        sc = qs->connection;\n\n        rev = sc->read;\n        rev->error = 1;\n        rev->ready = 1;\n\n        if (ngx_quic_control_flow(sc, f->final_size) != NGX_OK) {\n            goto cleanup;\n        }\n\n        qs->final_size = f->final_size;\n\n        if (ngx_quic_update_flow(sc, qs->final_size) != NGX_OK) {\n            goto cleanup;\n        }\n\n        return ngx_quic_init_stream(qs);\n    }\n\n    sc = qs->connection;\n\n    rev = sc->read;\n    rev->error = 1;\n    rev->ready = 1;\n\n    if (ngx_quic_control_flow(sc, f->final_size) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (qs->final_size != (uint64_t) -1 && qs->final_size != f->final_size) {\n        qc->error = NGX_QUIC_ERR_FINAL_SIZE_ERROR;\n        return NGX_ERROR;\n    }\n\n    if (qs->recv_last > f->final_size) {\n        qc->error = NGX_QUIC_ERR_FINAL_SIZE_ERROR;\n        return NGX_ERROR;\n    }\n\n    qs->final_size = f->final_size;\n\n    if (ngx_quic_update_flow(sc, qs->final_size) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (sc->sent > (off_t) qs->acked) {\n        qc->streams.sent -= (sc->sent - (off_t) qs->acked);\n        sc->sent = (off_t) qs->acked;\n    }\n\n    if (rev->active) {\n        ngx_post_event(rev, &ngx_posted_events);\n    }\n\n    return NGX_OK;\n\ncleanup:\n\n    pool = sc->pool;\n\n    ngx_close_connection(sc);\n    ngx_destroy_pool(pool);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_quic_handle_stop_sending_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_stop_sending_frame_t *f)\n{\n    ngx_pool_t             *pool;\n    ngx_event_t            *wev;\n    ngx_connection_t       *sc;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if ((f->id & NGX_QUIC_STREAM_UNIDIRECTIONAL)\n        && (f->id & NGX_QUIC_STREAM_SERVER_INITIATED) == 0)\n    {\n        qc->error = NGX_QUIC_ERR_STREAM_STATE_ERROR;\n        return NGX_ERROR;\n    }\n\n    qs = ngx_quic_find_stream(&qc->streams.tree, f->id);\n\n    if (qs == NULL) {\n        qs = ngx_quic_create_client_stream(c, f->id);\n\n        if (qs == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (qs == NGX_QUIC_STREAM_GONE) {\n            return NGX_OK;\n        }\n\n        sc = qs->connection;\n\n        if (ngx_quic_reset_stream(sc, f->error_code) != NGX_OK) {\n            pool = sc->pool;\n\n            ngx_close_connection(sc);\n            ngx_destroy_pool(pool);\n\n            return NGX_ERROR;\n        }\n\n        return ngx_quic_init_stream(qs);\n    }\n\n    if (qs->connection->sent > (off_t) qs->acked) {\n        qc->streams.sent -= (qs->connection->sent - (off_t) qs->acked);\n        qs->connection->sent = (off_t) qs->acked;\n    }\n\n    if (ngx_quic_reset_stream(qs->connection, f->error_code) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    wev = qs->connection->write;\n\n    if (wev->active) {\n        ngx_post_event(wev, &ngx_posted_events);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_handle_max_streams_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f)\n{\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    if (f->bidi) {\n        if (qc->streams.server_max_streams_bidi < f->limit) {\n            qc->streams.server_max_streams_bidi = f->limit;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic max_streams_bidi:%uL\", f->limit);\n        }\n\n    } else {\n        if (qc->streams.server_max_streams_uni < f->limit) {\n            qc->streams.server_max_streams_uni = f->limit;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"quic max_streams_uni:%uL\", f->limit);\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_handle_stream_ack(ngx_connection_t *c, ngx_quic_frame_t *f)\n{\n    uint64_t                sent, unacked;\n    ngx_event_t            *wev;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id);\n    if (qs == NULL) {\n        return;\n    }\n\n    wev = qs->connection->write;\n    sent = qs->connection->sent;\n    unacked = sent - qs->acked;\n\n    if (unacked >= qc->conf->stream_buf_size && wev->active) {\n        wev->ready = 1;\n        ngx_post_event(wev, &ngx_posted_events);\n    }\n\n    qs->acked += f->u.stream.length;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, qs->connection->log, 0,\n                   \"quic stream ack len:%uL acked:%uL unacked:%uL\",\n                   f->u.stream.length, qs->acked, sent - qs->acked);\n}\n\n\nstatic ngx_int_t\nngx_quic_control_flow(ngx_connection_t *c, uint64_t last)\n{\n    uint64_t                len;\n    ngx_event_t            *rev;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    rev = c->read;\n    qs = c->quic;\n    qc = ngx_quic_get_connection(qs->parent);\n\n    if (last <= qs->recv_last) {\n        return NGX_OK;\n    }\n\n    len = last - qs->recv_last;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic flow control msd:%uL/%uL md:%uL/%uL\",\n                   last, qs->recv_max_data, qc->streams.recv_last + len,\n                   qc->streams.recv_max_data);\n\n    qs->recv_last += len;\n\n    if (!rev->error && qs->recv_last > qs->recv_max_data) {\n        qc->error = NGX_QUIC_ERR_FLOW_CONTROL_ERROR;\n        return NGX_ERROR;\n    }\n\n    qc->streams.recv_last += len;\n\n    if (qc->streams.recv_last > qc->streams.recv_max_data) {\n        qc->error = NGX_QUIC_ERR_FLOW_CONTROL_ERROR;\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_update_flow(ngx_connection_t *c, uint64_t last)\n{\n    uint64_t                len;\n    ngx_event_t            *rev;\n    ngx_connection_t       *pc;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    rev = c->read;\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n\n    if (last <= qs->recv_offset) {\n        return NGX_OK;\n    }\n\n    len = last - qs->recv_offset;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic flow update %uL\", last);\n\n    qs->recv_offset += len;\n\n    if (!rev->pending_eof && !rev->error\n        && qs->recv_max_data <= qs->recv_offset + qs->recv_window / 2)\n    {\n        if (ngx_quic_update_max_stream_data(c) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    qc->streams.recv_offset += len;\n\n    if (qc->streams.recv_max_data\n        <= qc->streams.recv_offset + qc->streams.recv_window / 2)\n    {\n        if (ngx_quic_update_max_data(pc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_quic_add_exemptions(ngx_connection_t *c, size_t size)\n{\n    ngx_connection_t       *pc;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n\n    qc->streams.exemptions += size;\n}\n\n\nstatic ngx_int_t\nngx_quic_update_max_stream_data(ngx_connection_t *c)\n{\n    uint64_t                recv_max_data;\n    ngx_connection_t       *pc;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_stream_t      *qs;\n    ngx_quic_connection_t  *qc;\n\n    qs = c->quic;\n    pc = qs->parent;\n    qc = ngx_quic_get_connection(pc);\n\n    recv_max_data = qs->recv_offset + qs->recv_window;\n\n    if (qs->recv_max_data == recv_max_data) {\n        return NGX_OK;\n    }\n\n    qs->recv_max_data = recv_max_data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic flow update msd:%uL\", qs->recv_max_data);\n\n    frame = ngx_quic_alloc_frame(pc);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_MAX_STREAM_DATA;\n    frame->u.max_stream_data.id = qs->id;\n    frame->u.max_stream_data.limit = qs->recv_max_data;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_update_max_data(ngx_connection_t *c)\n{\n    uint64_t                recv_max_data;\n    ngx_quic_frame_t       *frame;\n    ngx_quic_connection_t  *qc;\n\n    qc = ngx_quic_get_connection(c);\n\n    recv_max_data = qc->streams.recv_offset + qc->streams.recv_window;\n\n    if (qc->streams.recv_max_data == recv_max_data) {\n        return NGX_OK;\n    }\n\n    qc->streams.recv_max_data = recv_max_data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic flow update md:%uL\", qc->streams.recv_max_data);\n\n    frame = ngx_quic_alloc_frame(c);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    frame->level = ssl_encryption_application;\n    frame->type = NGX_QUIC_FT_MAX_DATA;\n    frame->u.max_data.max_data = qc->streams.recv_max_data;\n\n    ngx_quic_queue_frame(qc, frame);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)\n{\n    if (!rev->active && !rev->ready) {\n        rev->active = 1;\n\n    } else if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {\n        rev->active = 0;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_handle_write_event(ngx_event_t *wev, size_t lowat)\n{\n    if (!wev->active && !wev->ready) {\n        wev->active = 1;\n\n    } else if (wev->active && wev->ready) {\n        wev->active = 0;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_streams.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_STREAMS_H_INCLUDED_\n#define _NGX_EVENT_QUIC_STREAMS_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);\nvoid ngx_quic_handle_stream_ack(ngx_connection_t *c,\n    ngx_quic_frame_t *f);\nngx_int_t ngx_quic_handle_max_data_frame(ngx_connection_t *c,\n    ngx_quic_max_data_frame_t *f);\nngx_int_t ngx_quic_handle_streams_blocked_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_streams_blocked_frame_t *f);\nngx_int_t ngx_quic_handle_data_blocked_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_data_blocked_frame_t *f);\nngx_int_t ngx_quic_handle_stream_data_blocked_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_stream_data_blocked_frame_t *f);\nngx_int_t ngx_quic_handle_max_stream_data_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_max_stream_data_frame_t *f);\nngx_int_t ngx_quic_handle_reset_stream_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_reset_stream_frame_t *f);\nngx_int_t ngx_quic_handle_stop_sending_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_stop_sending_frame_t *f);\nngx_int_t ngx_quic_handle_max_streams_frame(ngx_connection_t *c,\n    ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f);\n\nngx_int_t ngx_quic_init_streams(ngx_connection_t *c);\nvoid ngx_quic_rbtree_insert_stream(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nngx_quic_stream_t *ngx_quic_find_stream(ngx_rbtree_t *rbtree,\n    uint64_t id);\nngx_int_t ngx_quic_close_streams(ngx_connection_t *c,\n    ngx_quic_connection_t *qc);\n\n#endif /* _NGX_EVENT_QUIC_STREAMS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_tokens.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#include <ngx_sha1.h>\n#include <ngx_event_quic_connection.h>\n\n\n#define NGX_QUIC_MAX_TOKEN_SIZE              64\n    /* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */\n\n/* RFC 3602, 2.1 and 2.4 for AES-CBC block size and IV length */\n#define NGX_QUIC_AES_256_CBC_IV_LEN          16\n#define NGX_QUIC_AES_256_CBC_BLOCK_SIZE      16\n\n\nstatic void ngx_quic_address_hash(struct sockaddr *sockaddr, socklen_t socklen,\n    ngx_uint_t no_port, u_char buf[20]);\n\n\nngx_int_t\nngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,\n    u_char *token)\n{\n    ngx_str_t tmp;\n\n    tmp.data = secret;\n    tmp.len = NGX_QUIC_SR_KEY_LEN;\n\n    if (ngx_quic_derive_key(c->log, \"sr_token_key\", &tmp, cid, token,\n                            NGX_QUIC_SR_TOKEN_LEN)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic stateless reset token %*xs\",\n                    (size_t) NGX_QUIC_SR_TOKEN_LEN, token);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_new_token(ngx_connection_t *c, struct sockaddr *sockaddr,\n    socklen_t socklen, u_char *key, ngx_str_t *token, ngx_str_t *odcid,\n    time_t exp, ngx_uint_t is_retry)\n{\n    int                len, iv_len;\n    u_char            *p, *iv;\n    EVP_CIPHER_CTX    *ctx;\n    const EVP_CIPHER  *cipher;\n\n    u_char             in[NGX_QUIC_MAX_TOKEN_SIZE];\n\n    ngx_quic_address_hash(sockaddr, socklen, !is_retry, in);\n\n    p = in + 20;\n\n    p = ngx_cpymem(p, &exp, sizeof(time_t));\n\n    *p++ = is_retry ? 1 : 0;\n\n    if (odcid) {\n        *p++ = odcid->len;\n        p = ngx_cpymem(p, odcid->data, odcid->len);\n\n    } else {\n        *p++ = 0;\n    }\n\n    len = p - in;\n\n    cipher = EVP_aes_256_cbc();\n    iv_len = NGX_QUIC_AES_256_CBC_IV_LEN;\n\n    token->len = iv_len + len + NGX_QUIC_AES_256_CBC_BLOCK_SIZE;\n    token->data = ngx_pnalloc(c->pool, token->len);\n    if (token->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx = EVP_CIPHER_CTX_new();\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    iv = token->data;\n\n    if (RAND_bytes(iv, iv_len) <= 0\n        || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))\n    {\n        EVP_CIPHER_CTX_free(ctx);\n        return NGX_ERROR;\n    }\n\n    token->len = iv_len;\n\n    if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        return NGX_ERROR;\n    }\n\n    token->len += len;\n\n    if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {\n        EVP_CIPHER_CTX_free(ctx);\n        return NGX_ERROR;\n    }\n\n    token->len += len;\n\n    EVP_CIPHER_CTX_free(ctx);\n\n#ifdef NGX_QUIC_DEBUG_PACKETS\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"quic new token len:%uz %xV\", token->len, token);\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_quic_address_hash(struct sockaddr *sockaddr, socklen_t socklen,\n    ngx_uint_t no_port, u_char buf[20])\n{\n    size_t                len;\n    u_char               *data;\n    ngx_sha1_t            sha1;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    len = (size_t) socklen;\n    data = (u_char *) sockaddr;\n\n    if (no_port) {\n        switch (sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) sockaddr;\n\n            len = sizeof(struct in6_addr);\n            data = sin6->sin6_addr.s6_addr;\n\n            break;\n#endif\n\n        case AF_INET:\n            sin = (struct sockaddr_in *) sockaddr;\n\n            len = sizeof(in_addr_t);\n            data = (u_char *) &sin->sin_addr;\n\n            break;\n        }\n    }\n\n    ngx_sha1_init(&sha1);\n    ngx_sha1_update(&sha1, data, len);\n    ngx_sha1_final(buf, &sha1);\n}\n\n\nngx_int_t\nngx_quic_validate_token(ngx_connection_t *c, u_char *key,\n    ngx_quic_header_t *pkt)\n{\n    int                len, tlen, iv_len;\n    u_char            *iv, *p;\n    time_t             now, exp;\n    size_t             total;\n    ngx_str_t          odcid;\n    EVP_CIPHER_CTX    *ctx;\n    const EVP_CIPHER  *cipher;\n\n    u_char             addr_hash[20];\n    u_char             tdec[NGX_QUIC_MAX_TOKEN_SIZE];\n\n    /* Retry token or NEW_TOKEN in a previous connection */\n\n    cipher = EVP_aes_256_cbc();\n    iv = pkt->token.data;\n    iv_len = NGX_QUIC_AES_256_CBC_IV_LEN;\n\n    /* sanity checks */\n\n    if (pkt->token.len < (size_t) iv_len + NGX_QUIC_AES_256_CBC_BLOCK_SIZE) {\n        goto garbage;\n    }\n\n    if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {\n        goto garbage;\n    }\n\n    ctx = EVP_CIPHER_CTX_new();\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {\n        EVP_CIPHER_CTX_free(ctx);\n        return NGX_ERROR;\n    }\n\n    p = pkt->token.data + iv_len;\n    len = pkt->token.len - iv_len;\n\n    if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {\n        EVP_CIPHER_CTX_free(ctx);\n        goto garbage;\n    }\n    total = len;\n\n    if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {\n        EVP_CIPHER_CTX_free(ctx);\n        goto garbage;\n    }\n    total += tlen;\n\n    EVP_CIPHER_CTX_free(ctx);\n\n    if (total < (20 + sizeof(time_t) + 2)) {\n        goto garbage;\n    }\n\n    p = tdec + 20;\n\n    ngx_memcpy(&exp, p, sizeof(time_t));\n    p += sizeof(time_t);\n\n    pkt->retried = (*p++ == 1);\n\n    ngx_quic_address_hash(c->sockaddr, c->socklen, !pkt->retried, addr_hash);\n\n    if (ngx_memcmp(tdec, addr_hash, 20) != 0) {\n        goto bad_token;\n    }\n\n    odcid.len = *p++;\n    if (odcid.len) {\n        if (odcid.len > NGX_QUIC_MAX_CID_LEN) {\n            goto bad_token;\n        }\n\n        if ((size_t)(tdec + total - p) < odcid.len) {\n            goto bad_token;\n        }\n\n        odcid.data = p;\n    }\n\n    now = ngx_time();\n\n    if (now > exp) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"quic expired token\");\n        return NGX_DECLINED;\n    }\n\n    if (odcid.len) {\n        pkt->odcid.len = odcid.len;\n        pkt->odcid.data = ngx_pstrdup(c->pool, &odcid);\n        if (pkt->odcid.data == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        pkt->odcid = pkt->dcid;\n    }\n\n    pkt->validated = 1;\n\n    return NGX_OK;\n\ngarbage:\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0, \"quic garbage token\");\n\n    return NGX_ABORT;\n\nbad_token:\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0, \"quic invalid token\");\n\n    return NGX_DECLINED;\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_tokens.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_\n#define _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,\n    u_char *secret, u_char *token);\nngx_int_t ngx_quic_new_token(ngx_connection_t *c, struct sockaddr *sockaddr,\n    socklen_t socklen, u_char *key, ngx_str_t *token, ngx_str_t *odcid,\n    time_t expires, ngx_uint_t is_retry);\nngx_int_t ngx_quic_validate_token(ngx_connection_t *c,\n    u_char *key, ngx_quic_header_t *pkt);\n\n#endif /* _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_transport.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#include <ngx_event_quic_connection.h>\n\n\n#define NGX_QUIC_LONG_DCID_LEN_OFFSET  5\n#define NGX_QUIC_LONG_DCID_OFFSET      6\n#define NGX_QUIC_SHORT_DCID_OFFSET     1\n\n#define NGX_QUIC_STREAM_FRAME_FIN      0x01\n#define NGX_QUIC_STREAM_FRAME_LEN      0x02\n#define NGX_QUIC_STREAM_FRAME_OFF      0x04\n\n\n#if (NGX_HAVE_NONALIGNED)\n\n#define ngx_quic_parse_uint16(p)  ntohs(*(uint16_t *) (p))\n#define ngx_quic_parse_uint32(p)  ntohl(*(uint32_t *) (p))\n\n#define ngx_quic_write_uint16  ngx_quic_write_uint16_aligned\n#define ngx_quic_write_uint32  ngx_quic_write_uint32_aligned\n\n#else\n\n#define ngx_quic_parse_uint16(p)  ((p)[0] << 8 | (p)[1])\n#define ngx_quic_parse_uint32(p)                                              \\\n    ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])\n\n#define ngx_quic_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_quic_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_quic_write_uint64(p, s)                                           \\\n    ((p)[0] = (u_char) ((s) >> 56),                                           \\\n     (p)[1] = (u_char) ((s) >> 48),                                           \\\n     (p)[2] = (u_char) ((s) >> 40),                                           \\\n     (p)[3] = (u_char) ((s) >> 32),                                           \\\n     (p)[4] = (u_char) ((s) >> 24),                                           \\\n     (p)[5] = (u_char) ((s) >> 16),                                           \\\n     (p)[6] = (u_char) ((s) >> 8),                                            \\\n     (p)[7] = (u_char)  (s),                                                  \\\n     (p) + sizeof(uint64_t))\n\n#define ngx_quic_write_uint24(p, s)                                           \\\n    ((p)[0] = (u_char) ((s) >> 16),                                           \\\n     (p)[1] = (u_char) ((s) >> 8),                                            \\\n     (p)[2] = (u_char)  (s),                                                  \\\n     (p) + 3)\n\n#define ngx_quic_write_uint16_aligned(p, s)                                   \\\n    (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))\n\n#define ngx_quic_write_uint32_aligned(p, s)                                   \\\n    (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))\n\n#define ngx_quic_build_int_set(p, value, len, bits)                           \\\n    (*(p)++ = ((value >> ((len) * 8)) & 0xff) | ((bits) << 6))\n\n#define NGX_QUIC_VERSION(c)       (0xff000000 + (c))\n\n\nstatic u_char *ngx_quic_parse_int(u_char *pos, u_char *end, uint64_t *out);\nstatic ngx_uint_t ngx_quic_varint_len(uint64_t value);\nstatic void ngx_quic_build_int(u_char **pos, uint64_t value);\n\nstatic u_char *ngx_quic_read_uint8(u_char *pos, u_char *end, uint8_t *value);\nstatic u_char *ngx_quic_read_uint32(u_char *pos, u_char *end, uint32_t *value);\nstatic u_char *ngx_quic_read_bytes(u_char *pos, u_char *end, size_t len,\n    u_char **out);\nstatic u_char *ngx_quic_copy_bytes(u_char *pos, u_char *end, size_t len,\n    u_char *dst);\n\nstatic ngx_int_t ngx_quic_parse_short_header(ngx_quic_header_t *pkt,\n    size_t dcid_len);\nstatic ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt);\nstatic ngx_int_t ngx_quic_supported_version(uint32_t version);\nstatic ngx_int_t ngx_quic_parse_long_header_v1(ngx_quic_header_t *pkt);\n\nstatic size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,\n    u_char **pnp);\nstatic size_t ngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out,\n    u_char **pnp);\n\nstatic ngx_int_t ngx_quic_frame_allowed(ngx_quic_header_t *pkt,\n    ngx_uint_t frame_type);\nstatic size_t ngx_quic_create_ping(u_char *p);\nstatic size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack,\n    ngx_chain_t *ranges);\nstatic size_t ngx_quic_create_reset_stream(u_char *p,\n    ngx_quic_reset_stream_frame_t *rs);\nstatic size_t ngx_quic_create_stop_sending(u_char *p,\n    ngx_quic_stop_sending_frame_t *ss);\nstatic size_t ngx_quic_create_crypto(u_char *p,\n    ngx_quic_crypto_frame_t *crypto, ngx_chain_t *data);\nstatic size_t ngx_quic_create_hs_done(u_char *p);\nstatic size_t ngx_quic_create_new_token(u_char *p,\n    ngx_quic_new_token_frame_t *token);\nstatic size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf,\n    ngx_chain_t *data);\nstatic size_t ngx_quic_create_max_streams(u_char *p,\n    ngx_quic_max_streams_frame_t *ms);\nstatic size_t ngx_quic_create_max_stream_data(u_char *p,\n    ngx_quic_max_stream_data_frame_t *ms);\nstatic size_t ngx_quic_create_max_data(u_char *p,\n    ngx_quic_max_data_frame_t *md);\nstatic size_t ngx_quic_create_path_challenge(u_char *p,\n    ngx_quic_path_challenge_frame_t *pc);\nstatic size_t ngx_quic_create_path_response(u_char *p,\n    ngx_quic_path_challenge_frame_t *pc);\nstatic size_t ngx_quic_create_new_connection_id(u_char *p,\n    ngx_quic_new_conn_id_frame_t *rcid);\nstatic size_t ngx_quic_create_retire_connection_id(u_char *p,\n    ngx_quic_retire_cid_frame_t *rcid);\nstatic size_t ngx_quic_create_close(u_char *p, ngx_quic_frame_t *f);\n\nstatic ngx_int_t ngx_quic_parse_transport_param(u_char *p, u_char *end,\n    uint16_t id, ngx_quic_tp_t *dst);\n\n\nuint32_t  ngx_quic_versions[] = {\n    /* QUICv1 */\n    0x00000001,\n    NGX_QUIC_VERSION(29),\n    NGX_QUIC_VERSION(30),\n    NGX_QUIC_VERSION(31),\n    NGX_QUIC_VERSION(32),\n};\n\n#define NGX_QUIC_NVERSIONS \\\n    (sizeof(ngx_quic_versions) / sizeof(ngx_quic_versions[0]))\n\n\nstatic ngx_inline u_char *\nngx_quic_parse_int(u_char *pos, u_char *end, uint64_t *out)\n{\n    u_char      *p;\n    uint64_t     value;\n    ngx_uint_t   len;\n\n    if (pos >= end) {\n        return NULL;\n    }\n\n    p = pos;\n    len = 1 << (*p >> 6);\n\n    value = *p++ & 0x3f;\n\n    if ((size_t)(end - p) < (len - 1)) {\n        return NULL;\n    }\n\n    while (--len) {\n        value = (value << 8) + *p++;\n    }\n\n    *out = value;\n\n    return p;\n}\n\n\nstatic ngx_inline u_char *\nngx_quic_read_uint8(u_char *pos, u_char *end, uint8_t *value)\n{\n    if ((size_t)(end - pos) < 1) {\n        return NULL;\n    }\n\n    *value = *pos;\n\n    return pos + 1;\n}\n\n\nstatic ngx_inline u_char *\nngx_quic_read_uint32(u_char *pos, u_char *end, uint32_t *value)\n{\n    if ((size_t)(end - pos) < sizeof(uint32_t)) {\n        return NULL;\n    }\n\n    *value = ngx_quic_parse_uint32(pos);\n\n    return pos + sizeof(uint32_t);\n}\n\n\nstatic ngx_inline u_char *\nngx_quic_read_bytes(u_char *pos, u_char *end, size_t len, u_char **out)\n{\n    if ((size_t)(end - pos) < len) {\n        return NULL;\n    }\n\n    *out = pos;\n\n    return pos + len;\n}\n\n\nstatic u_char *\nngx_quic_copy_bytes(u_char *pos, u_char *end, size_t len, u_char *dst)\n{\n    if ((size_t)(end - pos) < len) {\n        return NULL;\n    }\n\n    ngx_memcpy(dst, pos, len);\n\n    return pos + len;\n}\n\n\nstatic ngx_inline ngx_uint_t\nngx_quic_varint_len(uint64_t value)\n{\n    if (value < (1 << 6)) {\n        return 1;\n    }\n\n    if (value < (1 << 14)) {\n        return 2;\n    }\n\n    if (value < (1 << 30)) {\n        return 4;\n    }\n\n    return 8;\n}\n\n\nstatic ngx_inline void\nngx_quic_build_int(u_char **pos, uint64_t value)\n{\n    u_char  *p;\n\n    p = *pos;\n\n    if (value < (1 << 6)) {\n        ngx_quic_build_int_set(p, value, 0, 0);\n\n    } else if (value < (1 << 14)) {\n        ngx_quic_build_int_set(p, value, 1, 1);\n        ngx_quic_build_int_set(p, value, 0, 0);\n\n    } else if (value < (1 << 30)) {\n        ngx_quic_build_int_set(p, value, 3, 2);\n        ngx_quic_build_int_set(p, value, 2, 0);\n        ngx_quic_build_int_set(p, value, 1, 0);\n        ngx_quic_build_int_set(p, value, 0, 0);\n\n    } else {\n        ngx_quic_build_int_set(p, value, 7, 3);\n        ngx_quic_build_int_set(p, value, 6, 0);\n        ngx_quic_build_int_set(p, value, 5, 0);\n        ngx_quic_build_int_set(p, value, 4, 0);\n        ngx_quic_build_int_set(p, value, 3, 0);\n        ngx_quic_build_int_set(p, value, 2, 0);\n        ngx_quic_build_int_set(p, value, 1, 0);\n        ngx_quic_build_int_set(p, value, 0, 0);\n    }\n\n    *pos = p;\n}\n\n\nngx_int_t\nngx_quic_parse_packet(ngx_quic_header_t *pkt)\n{\n    if (!ngx_quic_long_pkt(pkt->flags)) {\n        pkt->level = ssl_encryption_application;\n\n        if (ngx_quic_parse_short_header(pkt, NGX_QUIC_SERVER_CID_LEN) != NGX_OK)\n        {\n            return NGX_DECLINED;\n        }\n\n        return NGX_OK;\n    }\n\n    if (ngx_quic_parse_long_header(pkt) != NGX_OK) {\n        return NGX_DECLINED;\n    }\n\n    if (!ngx_quic_supported_version(pkt->version)) {\n        return NGX_ABORT;\n    }\n\n    if (ngx_quic_parse_long_header_v1(pkt) != NGX_OK) {\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_parse_short_header(ngx_quic_header_t *pkt, size_t dcid_len)\n{\n    u_char  *p, *end;\n\n    p = pkt->raw->pos;\n    end = pkt->data + pkt->len;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic packet rx short flags:%xd\", pkt->flags);\n\n    if (!(pkt->flags & NGX_QUIC_PKT_FIXED_BIT)) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0, \"quic fixed bit is not set\");\n        return NGX_ERROR;\n    }\n\n    pkt->dcid.len = dcid_len;\n\n    p = ngx_quic_read_bytes(p, end, dcid_len, &pkt->dcid.data);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet is too small to read dcid\");\n        return NGX_ERROR;\n    }\n\n    pkt->raw->pos = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_parse_long_header(ngx_quic_header_t *pkt)\n{\n    u_char   *p, *end;\n    uint8_t   idlen;\n\n    p = pkt->raw->pos;\n    end = pkt->data + pkt->len;\n\n    p = ngx_quic_read_uint32(p, end, &pkt->version);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet is too small to read version\");\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic packet rx long flags:%xd version:%xD\",\n                   pkt->flags, pkt->version);\n\n    if (!(pkt->flags & NGX_QUIC_PKT_FIXED_BIT)) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0, \"quic fixed bit is not set\");\n        return NGX_ERROR;\n    }\n\n    p = ngx_quic_read_uint8(p, end, &idlen);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet is too small to read dcid len\");\n        return NGX_ERROR;\n    }\n\n    if (idlen > NGX_QUIC_CID_LEN_MAX) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet dcid is too long\");\n        return NGX_ERROR;\n    }\n\n    pkt->dcid.len = idlen;\n\n    p = ngx_quic_read_bytes(p, end, idlen, &pkt->dcid.data);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet is too small to read dcid\");\n        return NGX_ERROR;\n    }\n\n    p = ngx_quic_read_uint8(p, end, &idlen);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet is too small to read scid len\");\n        return NGX_ERROR;\n    }\n\n    if (idlen > NGX_QUIC_CID_LEN_MAX) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet scid is too long\");\n        return NGX_ERROR;\n    }\n\n    pkt->scid.len = idlen;\n\n    p = ngx_quic_read_bytes(p, end, idlen, &pkt->scid.data);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic packet is too small to read scid\");\n        return NGX_ERROR;\n    }\n\n    pkt->raw->pos = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_quic_supported_version(uint32_t version)\n{\n    ngx_uint_t  i;\n\n    for (i = 0; i < NGX_QUIC_NVERSIONS; i++) {\n        if (ngx_quic_versions[i] == version) {\n            return 1;\n        }\n    }\n\n    return 0;\n}\n\n\nstatic ngx_int_t\nngx_quic_parse_long_header_v1(ngx_quic_header_t *pkt)\n{\n    u_char    *p, *end;\n    uint64_t   varint;\n\n    p = pkt->raw->pos;\n    end = pkt->raw->last;\n\n    pkt->log->action = \"parsing quic long header\";\n\n    if (ngx_quic_pkt_in(pkt->flags)) {\n\n        if (pkt->len < NGX_QUIC_MIN_INITIAL_SIZE) {\n            ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                          \"quic UDP datagram is too small for initial packet\");\n            return NGX_DECLINED;\n        }\n\n        p = ngx_quic_parse_int(p, end, &varint);\n        if (p == NULL) {\n            ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                          \"quic failed to parse token length\");\n            return NGX_ERROR;\n        }\n\n        pkt->token.len = varint;\n\n        p = ngx_quic_read_bytes(p, end, pkt->token.len, &pkt->token.data);\n        if (p == NULL) {\n            ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                          \"quic packet too small to read token data\");\n            return NGX_ERROR;\n        }\n\n        pkt->level = ssl_encryption_initial;\n\n    } else if (ngx_quic_pkt_zrtt(pkt->flags)) {\n        pkt->level = ssl_encryption_early_data;\n\n    } else if (ngx_quic_pkt_hs(pkt->flags)) {\n        pkt->level = ssl_encryption_handshake;\n\n    } else {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic bad packet type\");\n        return NGX_DECLINED;\n    }\n\n    p = ngx_quic_parse_int(p, end, &varint);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0, \"quic bad packet length\");\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                   \"quic packet rx %s len:%uL\",\n                   ngx_quic_level_name(pkt->level), varint);\n\n    if (varint > (uint64_t) ((pkt->data + pkt->len) - p)) {\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0, \"quic truncated %s packet\",\n                      ngx_quic_level_name(pkt->level));\n        return NGX_ERROR;\n    }\n\n    pkt->raw->pos = p;\n    pkt->len = p + varint - pkt->data;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_get_packet_dcid(ngx_log_t *log, u_char *data, size_t n,\n    ngx_str_t *dcid)\n{\n    size_t  len, offset;\n\n    if (n == 0) {\n        goto failed;\n    }\n\n    if (ngx_quic_long_pkt(*data)) {\n        if (n < NGX_QUIC_LONG_DCID_LEN_OFFSET + 1) {\n            goto failed;\n        }\n\n        len = data[NGX_QUIC_LONG_DCID_LEN_OFFSET];\n        offset = NGX_QUIC_LONG_DCID_OFFSET;\n\n    } else {\n        len = NGX_QUIC_SERVER_CID_LEN;\n        offset = NGX_QUIC_SHORT_DCID_OFFSET;\n    }\n\n    if (n < len + offset) {\n        goto failed;\n    }\n\n    dcid->len = len;\n    dcid->data = &data[offset];\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, log, 0, \"quic malformed packet\");\n\n    return NGX_ERROR;\n}\n\n\nsize_t\nngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out)\n{\n    u_char      *p, *start;\n    ngx_uint_t   i;\n\n    p = start = out;\n\n    *p++ = pkt->flags;\n\n    /*\n     * The Version field of a Version Negotiation packet\n     * MUST be set to 0x00000000\n     */\n    p = ngx_quic_write_uint32(p, 0);\n\n    *p++ = pkt->dcid.len;\n    p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);\n\n    *p++ = pkt->scid.len;\n    p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);\n\n    for (i = 0; i < NGX_QUIC_NVERSIONS; i++) {\n        p = ngx_quic_write_uint32(p, ngx_quic_versions[i]);\n    }\n\n    return p - start;\n}\n\n\n/* returns the amount of payload quic packet of \"pkt_len\" size may fit or 0 */\nsize_t\nngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len)\n{\n    size_t  len;\n\n    if ngx_quic_short_pkt(pkt->flags) {\n\n        len = 1 + pkt->dcid.len + pkt->num_len + EVP_GCM_TLS_TAG_LEN;\n        if (len > pkt_len) {\n            return 0;\n        }\n\n        return pkt_len - len;\n    }\n\n    /* flags, version, dcid and scid with lengths and zero-length token */\n    len = 5 + 2 + pkt->dcid.len + pkt->scid.len\n           + (pkt->level == ssl_encryption_initial ? 1 : 0);\n\n    if (len > pkt_len) {\n        return 0;\n    }\n\n    /* (pkt_len - len) is 'remainder' packet length (see RFC 9000, 17.2) */\n    len += ngx_quic_varint_len(pkt_len - len)\n           + pkt->num_len + EVP_GCM_TLS_TAG_LEN;\n\n    if (len > pkt_len) {\n        return 0;\n    }\n\n    return pkt_len - len;\n}\n\n\nsize_t\nngx_quic_create_header(ngx_quic_header_t *pkt, u_char *out, u_char **pnp)\n{\n    return ngx_quic_short_pkt(pkt->flags)\n           ? ngx_quic_create_short_header(pkt, out, pnp)\n           : ngx_quic_create_long_header(pkt, out, pnp);\n}\n\n\nstatic size_t\nngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,\n    u_char **pnp)\n{\n    size_t   rem_len;\n    u_char  *p, *start;\n\n    rem_len = pkt->num_len + pkt->payload.len + EVP_GCM_TLS_TAG_LEN;\n\n    if (out == NULL) {\n        return 5 + 2 + pkt->dcid.len + pkt->scid.len\n               + ngx_quic_varint_len(rem_len) + pkt->num_len\n               + (pkt->level == ssl_encryption_initial ? 1 : 0);\n    }\n\n    p = start = out;\n\n    *p++ = pkt->flags;\n\n    p = ngx_quic_write_uint32(p, pkt->version);\n\n    *p++ = pkt->dcid.len;\n    p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);\n\n    *p++ = pkt->scid.len;\n    p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);\n\n    if (pkt->level == ssl_encryption_initial) {\n        ngx_quic_build_int(&p, 0);\n    }\n\n    ngx_quic_build_int(&p, rem_len);\n\n    *pnp = p;\n\n    switch (pkt->num_len) {\n    case 1:\n        *p++ = pkt->trunc;\n        break;\n    case 2:\n        p = ngx_quic_write_uint16(p, pkt->trunc);\n        break;\n    case 3:\n        p = ngx_quic_write_uint24(p, pkt->trunc);\n        break;\n    case 4:\n        p = ngx_quic_write_uint32(p, pkt->trunc);\n        break;\n    }\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out,\n    u_char **pnp)\n{\n    u_char  *p, *start;\n\n    if (out == NULL) {\n        return 1 + pkt->dcid.len + pkt->num_len;\n    }\n\n    p = start = out;\n\n    *p++ = pkt->flags;\n\n    p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);\n\n    *pnp = p;\n\n    switch (pkt->num_len) {\n    case 1:\n        *p++ = pkt->trunc;\n        break;\n    case 2:\n        p = ngx_quic_write_uint16(p, pkt->trunc);\n        break;\n    case 3:\n        p = ngx_quic_write_uint24(p, pkt->trunc);\n        break;\n    case 4:\n        p = ngx_quic_write_uint32(p, pkt->trunc);\n        break;\n    }\n\n    return p - start;\n}\n\n\nsize_t\nngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out,\n    u_char **start)\n{\n    u_char  *p;\n\n    p = out;\n\n    *p++ = pkt->odcid.len;\n    p = ngx_cpymem(p, pkt->odcid.data, pkt->odcid.len);\n\n    *start = p;\n\n    *p++ = 0xff;\n\n    p = ngx_quic_write_uint32(p, pkt->version);\n\n    *p++ = pkt->dcid.len;\n    p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len);\n\n    *p++ = pkt->scid.len;\n    p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);\n\n    p = ngx_cpymem(p, pkt->token.data, pkt->token.len);\n\n    return p - out;\n}\n\n\nssize_t\nngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,\n    ngx_quic_frame_t *f)\n{\n    u_char      *p;\n    uint64_t     varint;\n    ngx_buf_t   *b;\n    ngx_uint_t   i;\n\n    b = f->data->buf;\n\n    p = start;\n\n    p = ngx_quic_parse_int(p, end, &varint);\n    if (p == NULL) {\n        pkt->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic failed to obtain quic frame type\");\n        return NGX_ERROR;\n    }\n\n    if (varint > NGX_QUIC_FT_LAST) {\n        pkt->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic unknown frame type 0x%xL\", varint);\n        return NGX_ERROR;\n    }\n\n    f->type = varint;\n\n    if (ngx_quic_frame_allowed(pkt, f->type) != NGX_OK) {\n        pkt->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;\n        return NGX_ERROR;\n    }\n\n    switch (f->type) {\n\n    case NGX_QUIC_FT_CRYPTO:\n\n        p = ngx_quic_parse_int(p, end, &f->u.crypto.offset);\n        if (p == NULL) {\n            goto error;\n        }\n\n        p = ngx_quic_parse_int(p, end, &f->u.crypto.length);\n        if (p == NULL) {\n            goto error;\n        }\n\n        p = ngx_quic_read_bytes(p, end, f->u.crypto.length, &b->pos);\n        if (p == NULL) {\n            goto error;\n        }\n\n        b->last = p;\n\n        break;\n\n    case NGX_QUIC_FT_PADDING:\n\n        while (p < end && *p == NGX_QUIC_FT_PADDING) {\n            p++;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_ACK:\n    case NGX_QUIC_FT_ACK_ECN:\n\n        if (!((p = ngx_quic_parse_int(p, end, &f->u.ack.largest))\n              && (p = ngx_quic_parse_int(p, end, &f->u.ack.delay))\n              && (p = ngx_quic_parse_int(p, end, &f->u.ack.range_count))\n              && (p = ngx_quic_parse_int(p, end, &f->u.ack.first_range))))\n        {\n            goto error;\n        }\n\n        b->pos = p;\n\n        /* process all ranges to get bounds, values are ignored */\n        for (i = 0; i < f->u.ack.range_count; i++) {\n\n            p = ngx_quic_parse_int(p, end, &varint);\n            if (p) {\n                p = ngx_quic_parse_int(p, end, &varint);\n            }\n\n            if (p == NULL) {\n                goto error;\n            }\n        }\n\n        b->last = p;\n\n        f->u.ack.ranges_length = b->last - b->pos;\n\n        if (f->type == NGX_QUIC_FT_ACK_ECN) {\n\n            if (!((p = ngx_quic_parse_int(p, end, &f->u.ack.ect0))\n                  && (p = ngx_quic_parse_int(p, end, &f->u.ack.ect1))\n                  && (p = ngx_quic_parse_int(p, end, &f->u.ack.ce))))\n            {\n                goto error;\n            }\n\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pkt->log, 0,\n                           \"quic ACK ECN counters ect0:%uL ect1:%uL ce:%uL\",\n                           f->u.ack.ect0, f->u.ack.ect1, f->u.ack.ce);\n        }\n\n        break;\n\n    case NGX_QUIC_FT_PING:\n        break;\n\n    case NGX_QUIC_FT_NEW_CONNECTION_ID:\n\n        p = ngx_quic_parse_int(p, end, &f->u.ncid.seqnum);\n        if (p == NULL) {\n            goto error;\n        }\n\n        p = ngx_quic_parse_int(p, end, &f->u.ncid.retire);\n        if (p == NULL) {\n            goto error;\n        }\n\n        if (f->u.ncid.retire > f->u.ncid.seqnum) {\n            goto error;\n        }\n\n        p = ngx_quic_read_uint8(p, end, &f->u.ncid.len);\n        if (p == NULL) {\n            goto error;\n        }\n\n        if (f->u.ncid.len < 1 || f->u.ncid.len > NGX_QUIC_CID_LEN_MAX) {\n            goto error;\n        }\n\n        p = ngx_quic_copy_bytes(p, end, f->u.ncid.len, f->u.ncid.cid);\n        if (p == NULL) {\n            goto error;\n        }\n\n        p = ngx_quic_copy_bytes(p, end, NGX_QUIC_SR_TOKEN_LEN, f->u.ncid.srt);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_RETIRE_CONNECTION_ID:\n\n        p = ngx_quic_parse_int(p, end, &f->u.retire_cid.sequence_number);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_CONNECTION_CLOSE:\n    case NGX_QUIC_FT_CONNECTION_CLOSE_APP:\n\n        p = ngx_quic_parse_int(p, end, &f->u.close.error_code);\n        if (p == NULL) {\n            goto error;\n        }\n\n        if (f->type == NGX_QUIC_FT_CONNECTION_CLOSE) {\n            p = ngx_quic_parse_int(p, end, &f->u.close.frame_type);\n            if (p == NULL) {\n                goto error;\n            }\n        }\n\n        p = ngx_quic_parse_int(p, end, &varint);\n        if (p == NULL) {\n            goto error;\n        }\n\n        f->u.close.reason.len = varint;\n\n        p = ngx_quic_read_bytes(p, end, f->u.close.reason.len,\n                                &f->u.close.reason.data);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_STREAM:\n    case NGX_QUIC_FT_STREAM1:\n    case NGX_QUIC_FT_STREAM2:\n    case NGX_QUIC_FT_STREAM3:\n    case NGX_QUIC_FT_STREAM4:\n    case NGX_QUIC_FT_STREAM5:\n    case NGX_QUIC_FT_STREAM6:\n    case NGX_QUIC_FT_STREAM7:\n\n        f->u.stream.fin = (f->type & NGX_QUIC_STREAM_FRAME_FIN) ? 1 : 0;\n\n        p = ngx_quic_parse_int(p, end, &f->u.stream.stream_id);\n        if (p == NULL) {\n            goto error;\n        }\n\n        if (f->type & NGX_QUIC_STREAM_FRAME_OFF) {\n            f->u.stream.off = 1;\n\n            p = ngx_quic_parse_int(p, end, &f->u.stream.offset);\n            if (p == NULL) {\n                goto error;\n            }\n\n        } else {\n            f->u.stream.off = 0;\n            f->u.stream.offset = 0;\n        }\n\n        if (f->type & NGX_QUIC_STREAM_FRAME_LEN) {\n            f->u.stream.len = 1;\n\n            p = ngx_quic_parse_int(p, end, &f->u.stream.length);\n            if (p == NULL) {\n                goto error;\n            }\n\n        } else {\n            f->u.stream.len = 0;\n            f->u.stream.length = end - p; /* up to packet end */\n        }\n\n        p = ngx_quic_read_bytes(p, end, f->u.stream.length, &b->pos);\n        if (p == NULL) {\n            goto error;\n        }\n\n        b->last = p;\n\n        f->type = NGX_QUIC_FT_STREAM;\n        break;\n\n    case NGX_QUIC_FT_MAX_DATA:\n\n        p = ngx_quic_parse_int(p, end, &f->u.max_data.max_data);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_RESET_STREAM:\n\n        if (!((p = ngx_quic_parse_int(p, end, &f->u.reset_stream.id))\n              && (p = ngx_quic_parse_int(p, end, &f->u.reset_stream.error_code))\n              && (p = ngx_quic_parse_int(p, end,\n                                         &f->u.reset_stream.final_size))))\n        {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_STOP_SENDING:\n\n        p = ngx_quic_parse_int(p, end, &f->u.stop_sending.id);\n        if (p == NULL) {\n            goto error;\n        }\n\n        p = ngx_quic_parse_int(p, end, &f->u.stop_sending.error_code);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_STREAMS_BLOCKED:\n    case NGX_QUIC_FT_STREAMS_BLOCKED2:\n\n        p = ngx_quic_parse_int(p, end, &f->u.streams_blocked.limit);\n        if (p == NULL) {\n            goto error;\n        }\n\n        if (f->u.streams_blocked.limit > 0x1000000000000000) {\n            goto error;\n        }\n\n        f->u.streams_blocked.bidi =\n                              (f->type == NGX_QUIC_FT_STREAMS_BLOCKED) ? 1 : 0;\n        break;\n\n    case NGX_QUIC_FT_MAX_STREAMS:\n    case NGX_QUIC_FT_MAX_STREAMS2:\n\n        p = ngx_quic_parse_int(p, end, &f->u.max_streams.limit);\n        if (p == NULL) {\n            goto error;\n        }\n\n        if (f->u.max_streams.limit > 0x1000000000000000) {\n            goto error;\n        }\n\n        f->u.max_streams.bidi = (f->type == NGX_QUIC_FT_MAX_STREAMS) ? 1 : 0;\n\n        break;\n\n    case NGX_QUIC_FT_MAX_STREAM_DATA:\n\n        p = ngx_quic_parse_int(p, end, &f->u.max_stream_data.id);\n        if (p == NULL) {\n            goto error;\n        }\n\n        p = ngx_quic_parse_int(p, end,  &f->u.max_stream_data.limit);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_DATA_BLOCKED:\n\n        p = ngx_quic_parse_int(p, end, &f->u.data_blocked.limit);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_STREAM_DATA_BLOCKED:\n\n        p = ngx_quic_parse_int(p, end, &f->u.stream_data_blocked.id);\n        if (p == NULL) {\n            goto error;\n        }\n\n        p = ngx_quic_parse_int(p, end, &f->u.stream_data_blocked.limit);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_PATH_CHALLENGE:\n\n        p = ngx_quic_copy_bytes(p, end, 8, f->u.path_challenge.data);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    case NGX_QUIC_FT_PATH_RESPONSE:\n\n        p = ngx_quic_copy_bytes(p, end, 8, f->u.path_response.data);\n        if (p == NULL) {\n            goto error;\n        }\n\n        break;\n\n    default:\n        ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                      \"quic unknown frame type 0x%xi\", f->type);\n        return NGX_ERROR;\n    }\n\n    f->level = pkt->level;\n\n    return p - start;\n\nerror:\n\n    pkt->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;\n\n    ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                  \"quic failed to parse frame type:0x%xi\", f->type);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_quic_frame_allowed(ngx_quic_header_t *pkt, ngx_uint_t frame_type)\n{\n    uint8_t  ptype;\n\n    /*\n     * RFC 9000, 12.4. Frames and Frame Types: Table 3\n     *\n     * Frame permissions per packet: 4 bits: IH01\n     */\n    static uint8_t ngx_quic_frame_masks[] = {\n         /* PADDING  */              0xF,\n         /* PING */                  0xF,\n         /* ACK */                   0xD,\n         /* ACK_ECN */               0xD,\n         /* RESET_STREAM */          0x3,\n         /* STOP_SENDING */          0x3,\n         /* CRYPTO */                0xD,\n         /* NEW_TOKEN */             0x0, /* only sent by server */\n         /* STREAM */                0x3,\n         /* STREAM1 */               0x3,\n         /* STREAM2 */               0x3,\n         /* STREAM3 */               0x3,\n         /* STREAM4 */               0x3,\n         /* STREAM5 */               0x3,\n         /* STREAM6 */               0x3,\n         /* STREAM7 */               0x3,\n         /* MAX_DATA */              0x3,\n         /* MAX_STREAM_DATA */       0x3,\n         /* MAX_STREAMS */           0x3,\n         /* MAX_STREAMS2 */          0x3,\n         /* DATA_BLOCKED */          0x3,\n         /* STREAM_DATA_BLOCKED */   0x3,\n         /* STREAMS_BLOCKED */       0x3,\n         /* STREAMS_BLOCKED2 */      0x3,\n         /* NEW_CONNECTION_ID */     0x3,\n         /* RETIRE_CONNECTION_ID */  0x3,\n         /* PATH_CHALLENGE */        0x3,\n         /* PATH_RESPONSE */         0x1,\n         /* CONNECTION_CLOSE */      0xF,\n         /* CONNECTION_CLOSE2 */     0x3,\n         /* HANDSHAKE_DONE */        0x0, /* only sent by server */\n    };\n\n    if (ngx_quic_long_pkt(pkt->flags)) {\n\n        if (ngx_quic_pkt_in(pkt->flags)) {\n            ptype = 8; /* initial */\n\n        } else if (ngx_quic_pkt_hs(pkt->flags)) {\n            ptype = 4; /* handshake */\n\n        } else {\n            ptype = 2; /* zero-rtt */\n        }\n\n    } else {\n        ptype = 1; /* application data */\n    }\n\n    if (ptype & ngx_quic_frame_masks[frame_type]) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, pkt->log, 0,\n                  \"quic frame type 0x%xi is not \"\n                  \"allowed in packet with flags 0x%xd\",\n                  frame_type, pkt->flags);\n\n    return NGX_DECLINED;\n}\n\n\nssize_t\nngx_quic_parse_ack_range(ngx_log_t *log, u_char *start, u_char *end,\n    uint64_t *gap, uint64_t *range)\n{\n    u_char  *p;\n\n    p = start;\n\n    p = ngx_quic_parse_int(p, end, gap);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, log, 0,\n                      \"quic failed to parse ack frame gap\");\n        return NGX_ERROR;\n    }\n\n    p = ngx_quic_parse_int(p, end, range);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_INFO, log, 0,\n                      \"quic failed to parse ack frame range\");\n        return NGX_ERROR;\n    }\n\n    return p - start;\n}\n\n\nsize_t\nngx_quic_create_ack_range(u_char *p, uint64_t gap, uint64_t range)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(gap);\n        len += ngx_quic_varint_len(range);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, gap);\n    ngx_quic_build_int(&p, range);\n\n    return p - start;\n}\n\n\nssize_t\nngx_quic_create_frame(u_char *p, ngx_quic_frame_t *f)\n{\n    /*\n     *  RFC 9002, 2.  Conventions and Definitions\n     *\n     *  Ack-eliciting frames:  All frames other than ACK, PADDING, and\n     *  CONNECTION_CLOSE are considered ack-eliciting.\n     */\n    f->need_ack = 1;\n\n    switch (f->type) {\n    case NGX_QUIC_FT_PING:\n        return ngx_quic_create_ping(p);\n\n    case NGX_QUIC_FT_ACK:\n        f->need_ack = 0;\n        return ngx_quic_create_ack(p, &f->u.ack, f->data);\n\n    case NGX_QUIC_FT_RESET_STREAM:\n        return ngx_quic_create_reset_stream(p, &f->u.reset_stream);\n\n    case NGX_QUIC_FT_STOP_SENDING:\n        return ngx_quic_create_stop_sending(p, &f->u.stop_sending);\n\n    case NGX_QUIC_FT_CRYPTO:\n        return ngx_quic_create_crypto(p, &f->u.crypto, f->data);\n\n    case NGX_QUIC_FT_HANDSHAKE_DONE:\n        return ngx_quic_create_hs_done(p);\n\n    case NGX_QUIC_FT_NEW_TOKEN:\n        return ngx_quic_create_new_token(p, &f->u.token);\n\n    case NGX_QUIC_FT_STREAM:\n        return ngx_quic_create_stream(p, &f->u.stream, f->data);\n\n    case NGX_QUIC_FT_CONNECTION_CLOSE:\n    case NGX_QUIC_FT_CONNECTION_CLOSE_APP:\n        f->need_ack = 0;\n        return ngx_quic_create_close(p, f);\n\n    case NGX_QUIC_FT_MAX_STREAMS:\n        return ngx_quic_create_max_streams(p, &f->u.max_streams);\n\n    case NGX_QUIC_FT_MAX_STREAM_DATA:\n        return ngx_quic_create_max_stream_data(p, &f->u.max_stream_data);\n\n    case NGX_QUIC_FT_MAX_DATA:\n        return ngx_quic_create_max_data(p, &f->u.max_data);\n\n    case NGX_QUIC_FT_PATH_CHALLENGE:\n        return ngx_quic_create_path_challenge(p, &f->u.path_challenge);\n\n    case NGX_QUIC_FT_PATH_RESPONSE:\n        return ngx_quic_create_path_response(p, &f->u.path_response);\n\n    case NGX_QUIC_FT_NEW_CONNECTION_ID:\n        return ngx_quic_create_new_connection_id(p, &f->u.ncid);\n\n    case NGX_QUIC_FT_RETIRE_CONNECTION_ID:\n        return ngx_quic_create_retire_connection_id(p, &f->u.retire_cid);\n\n    default:\n        /* BUG: unsupported frame type generated */\n        return NGX_ERROR;\n    }\n}\n\n\nstatic size_t\nngx_quic_create_ping(u_char *p)\n{\n    u_char  *start;\n\n    if (p == NULL) {\n        return ngx_quic_varint_len(NGX_QUIC_FT_PING);\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_PING);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack, ngx_chain_t *ranges)\n{\n    size_t      len;\n    u_char     *start;\n    ngx_buf_t  *b;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_ACK);\n        len += ngx_quic_varint_len(ack->largest);\n        len += ngx_quic_varint_len(ack->delay);\n        len += ngx_quic_varint_len(ack->range_count);\n        len += ngx_quic_varint_len(ack->first_range);\n        len += ack->ranges_length;\n\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_ACK);\n    ngx_quic_build_int(&p, ack->largest);\n    ngx_quic_build_int(&p, ack->delay);\n    ngx_quic_build_int(&p, ack->range_count);\n    ngx_quic_build_int(&p, ack->first_range);\n\n    while (ranges) {\n        b = ranges->buf;\n        p = ngx_cpymem(p, b->pos, b->last - b->pos);\n        ranges = ranges->next;\n    }\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_reset_stream(u_char *p, ngx_quic_reset_stream_frame_t *rs)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_RESET_STREAM);\n        len += ngx_quic_varint_len(rs->id);\n        len += ngx_quic_varint_len(rs->error_code);\n        len += ngx_quic_varint_len(rs->final_size);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_RESET_STREAM);\n    ngx_quic_build_int(&p, rs->id);\n    ngx_quic_build_int(&p, rs->error_code);\n    ngx_quic_build_int(&p, rs->final_size);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_stop_sending(u_char *p, ngx_quic_stop_sending_frame_t *ss)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_STOP_SENDING);\n        len += ngx_quic_varint_len(ss->id);\n        len += ngx_quic_varint_len(ss->error_code);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_STOP_SENDING);\n    ngx_quic_build_int(&p, ss->id);\n    ngx_quic_build_int(&p, ss->error_code);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto,\n    ngx_chain_t *data)\n{\n    size_t      len;\n    u_char     *start;\n    ngx_buf_t  *b;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_CRYPTO);\n        len += ngx_quic_varint_len(crypto->offset);\n        len += ngx_quic_varint_len(crypto->length);\n        len += crypto->length;\n\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO);\n    ngx_quic_build_int(&p, crypto->offset);\n    ngx_quic_build_int(&p, crypto->length);\n\n    while (data) {\n        b = data->buf;\n        p = ngx_cpymem(p, b->pos, b->last - b->pos);\n        data = data->next;\n    }\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_hs_done(u_char *p)\n{\n    u_char  *start;\n\n    if (p == NULL) {\n        return ngx_quic_varint_len(NGX_QUIC_FT_HANDSHAKE_DONE);\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_HANDSHAKE_DONE);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_new_token(u_char *p, ngx_quic_new_token_frame_t *token)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_NEW_TOKEN);\n        len += ngx_quic_varint_len(token->length);\n        len += token->length;\n\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_NEW_TOKEN);\n    ngx_quic_build_int(&p, token->length);\n    p = ngx_cpymem(p, token->data, token->length);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf,\n    ngx_chain_t *data)\n{\n    size_t      len;\n    u_char     *start, type;\n    ngx_buf_t  *b;\n\n    type = NGX_QUIC_FT_STREAM;\n\n    if (sf->off) {\n        type |= NGX_QUIC_STREAM_FRAME_OFF;\n    }\n\n    if (sf->len) {\n        type |= NGX_QUIC_STREAM_FRAME_LEN;\n    }\n\n    if (sf->fin) {\n        type |= NGX_QUIC_STREAM_FRAME_FIN;\n    }\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(type);\n        len += ngx_quic_varint_len(sf->stream_id);\n\n        if (sf->off) {\n            len += ngx_quic_varint_len(sf->offset);\n        }\n\n        if (sf->len) {\n            len += ngx_quic_varint_len(sf->length);\n        }\n\n        len += sf->length;\n\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, type);\n    ngx_quic_build_int(&p, sf->stream_id);\n\n    if (sf->off) {\n        ngx_quic_build_int(&p, sf->offset);\n    }\n\n    if (sf->len) {\n        ngx_quic_build_int(&p, sf->length);\n    }\n\n    while (data) {\n        b = data->buf;\n        p = ngx_cpymem(p, b->pos, b->last - b->pos);\n        data = data->next;\n    }\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_max_streams(u_char *p, ngx_quic_max_streams_frame_t *ms)\n{\n    size_t       len;\n    u_char      *start;\n    ngx_uint_t   type;\n\n    type = ms->bidi ?  NGX_QUIC_FT_MAX_STREAMS : NGX_QUIC_FT_MAX_STREAMS2;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(type);\n        len += ngx_quic_varint_len(ms->limit);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, type);\n    ngx_quic_build_int(&p, ms->limit);\n\n    return p - start;\n}\n\n\nstatic ngx_int_t\nngx_quic_parse_transport_param(u_char *p, u_char *end, uint16_t id,\n    ngx_quic_tp_t *dst)\n{\n    uint64_t   varint;\n    ngx_str_t  str;\n\n    varint = 0;\n    ngx_str_null(&str);\n\n    switch (id) {\n\n    case NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION:\n        /* zero-length option */\n        if (end - p != 0) {\n            return NGX_ERROR;\n        }\n        dst->disable_active_migration = 1;\n        return NGX_OK;\n\n    case NGX_QUIC_TP_MAX_IDLE_TIMEOUT:\n    case NGX_QUIC_TP_MAX_UDP_PAYLOAD_SIZE:\n    case NGX_QUIC_TP_INITIAL_MAX_DATA:\n    case NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:\n    case NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:\n    case NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI:\n    case NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI:\n    case NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI:\n    case NGX_QUIC_TP_ACK_DELAY_EXPONENT:\n    case NGX_QUIC_TP_MAX_ACK_DELAY:\n    case NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT:\n\n        p = ngx_quic_parse_int(p, end, &varint);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n        break;\n\n    case NGX_QUIC_TP_INITIAL_SCID:\n\n        str.len = end - p;\n        str.data = p;\n        break;\n\n    default:\n        return NGX_DECLINED;\n    }\n\n    switch (id) {\n\n    case NGX_QUIC_TP_MAX_IDLE_TIMEOUT:\n        dst->max_idle_timeout = varint;\n        break;\n\n    case NGX_QUIC_TP_MAX_UDP_PAYLOAD_SIZE:\n        dst->max_udp_payload_size = varint;\n        break;\n\n    case NGX_QUIC_TP_INITIAL_MAX_DATA:\n        dst->initial_max_data = varint;\n        break;\n\n    case NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:\n        dst->initial_max_stream_data_bidi_local = varint;\n        break;\n\n    case NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:\n        dst->initial_max_stream_data_bidi_remote = varint;\n        break;\n\n    case NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI:\n        dst->initial_max_stream_data_uni = varint;\n        break;\n\n    case NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI:\n        dst->initial_max_streams_bidi = varint;\n        break;\n\n    case NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI:\n        dst->initial_max_streams_uni = varint;\n        break;\n\n    case NGX_QUIC_TP_ACK_DELAY_EXPONENT:\n        dst->ack_delay_exponent = varint;\n        break;\n\n    case NGX_QUIC_TP_MAX_ACK_DELAY:\n        dst->max_ack_delay = varint;\n        break;\n\n    case NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT:\n        dst->active_connection_id_limit = varint;\n        break;\n\n    case NGX_QUIC_TP_INITIAL_SCID:\n        dst->initial_scid = str;\n        break;\n\n    default:\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp,\n    ngx_log_t *log)\n{\n    uint64_t   id, len;\n    ngx_int_t  rc;\n\n    while (p < end) {\n        p = ngx_quic_parse_int(p, end, &id);\n        if (p == NULL) {\n            ngx_log_error(NGX_LOG_INFO, log, 0,\n                          \"quic failed to parse transport param id\");\n            return NGX_ERROR;\n        }\n\n        switch (id) {\n        case NGX_QUIC_TP_ORIGINAL_DCID:\n        case NGX_QUIC_TP_PREFERRED_ADDRESS:\n        case NGX_QUIC_TP_RETRY_SCID:\n        case NGX_QUIC_TP_SR_TOKEN:\n            ngx_log_error(NGX_LOG_INFO, log, 0,\n                          \"quic client sent forbidden transport param\"\n                          \" id:0x%xL\", id);\n            return NGX_ERROR;\n        }\n\n        p = ngx_quic_parse_int(p, end, &len);\n        if (p == NULL) {\n            ngx_log_error(NGX_LOG_INFO, log, 0,\n                          \"quic failed to parse\"\n                          \" transport param id:0x%xL length\", id);\n            return NGX_ERROR;\n        }\n\n        rc = ngx_quic_parse_transport_param(p, p + len, id, tp);\n\n        if (rc == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_INFO, log, 0,\n                          \"quic failed to parse\"\n                          \" transport param id:0x%xL data\", id);\n            return NGX_ERROR;\n        }\n\n        if (rc == NGX_DECLINED) {\n            ngx_log_error(NGX_LOG_INFO, log, 0,\n                          \"quic %s transport param id:0x%xL, skipped\",\n                          (id % 31 == 27) ? \"reserved\" : \"unknown\", id);\n        }\n\n        p += len;\n    }\n\n    if (p != end) {\n        ngx_log_error(NGX_LOG_INFO, log, 0,\n                      \"quic trailing garbage in\"\n                      \" transport parameters: bytes:%ui\",\n                      end - p);\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic transport parameters parsed ok\");\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp disable active migration: %ui\",\n                   tp->disable_active_migration);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, \"quic tp idle_timeout:%ui\",\n                   tp->max_idle_timeout);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp max_udp_payload_size:%ui\",\n                   tp->max_udp_payload_size);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, \"quic tp max_data:%ui\",\n                   tp->initial_max_data);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp max_stream_data_bidi_local:%ui\",\n                   tp->initial_max_stream_data_bidi_local);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp max_stream_data_bidi_remote:%ui\",\n                   tp->initial_max_stream_data_bidi_remote);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp max_stream_data_uni:%ui\",\n                   tp->initial_max_stream_data_uni);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp initial_max_streams_bidi:%ui\",\n                   tp->initial_max_streams_bidi);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp initial_max_streams_uni:%ui\",\n                   tp->initial_max_streams_uni);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp ack_delay_exponent:%ui\",\n                   tp->ack_delay_exponent);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, \"quic tp max_ack_delay:%ui\",\n                   tp->max_ack_delay);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp active_connection_id_limit:%ui\",\n                   tp->active_connection_id_limit);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"quic tp initial source_connection_id len:%uz %xV\",\n                   tp->initial_scid.len, &tp->initial_scid);\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_quic_create_max_stream_data(u_char *p, ngx_quic_max_stream_data_frame_t *ms)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_MAX_STREAM_DATA);\n        len += ngx_quic_varint_len(ms->id);\n        len += ngx_quic_varint_len(ms->limit);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_MAX_STREAM_DATA);\n    ngx_quic_build_int(&p, ms->id);\n    ngx_quic_build_int(&p, ms->limit);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_max_data(u_char *p, ngx_quic_max_data_frame_t *md)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_MAX_DATA);\n        len += ngx_quic_varint_len(md->max_data);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_MAX_DATA);\n    ngx_quic_build_int(&p, md->max_data);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_path_challenge(u_char *p, ngx_quic_path_challenge_frame_t *pc)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_PATH_CHALLENGE);\n        len += sizeof(pc->data);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_PATH_CHALLENGE);\n    p = ngx_cpymem(p, &pc->data, sizeof(pc->data));\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_path_response(u_char *p, ngx_quic_path_challenge_frame_t *pc)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_PATH_RESPONSE);\n        len += sizeof(pc->data);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_PATH_RESPONSE);\n    p = ngx_cpymem(p, &pc->data, sizeof(pc->data));\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_new_connection_id(u_char *p, ngx_quic_new_conn_id_frame_t *ncid)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_NEW_CONNECTION_ID);\n        len += ngx_quic_varint_len(ncid->seqnum);\n        len += ngx_quic_varint_len(ncid->retire);\n        len++;\n        len += ncid->len;\n        len += NGX_QUIC_SR_TOKEN_LEN;\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_NEW_CONNECTION_ID);\n    ngx_quic_build_int(&p, ncid->seqnum);\n    ngx_quic_build_int(&p, ncid->retire);\n    *p++ = ncid->len;\n    p = ngx_cpymem(p, ncid->cid, ncid->len);\n    p = ngx_cpymem(p, ncid->srt, NGX_QUIC_SR_TOKEN_LEN);\n\n    return p - start;\n}\n\n\nstatic size_t\nngx_quic_create_retire_connection_id(u_char *p,\n    ngx_quic_retire_cid_frame_t *rcid)\n{\n    size_t   len;\n    u_char  *start;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(NGX_QUIC_FT_RETIRE_CONNECTION_ID);\n        len += ngx_quic_varint_len(rcid->sequence_number);\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, NGX_QUIC_FT_RETIRE_CONNECTION_ID);\n    ngx_quic_build_int(&p, rcid->sequence_number);\n\n    return p - start;\n}\n\n\nssize_t\nngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp,\n    size_t *clen)\n{\n    u_char  *p;\n    size_t   len;\n\n#define ngx_quic_tp_len(id, value)                                            \\\n    ngx_quic_varint_len(id)                                                   \\\n    + ngx_quic_varint_len(value)                                              \\\n    + ngx_quic_varint_len(ngx_quic_varint_len(value))\n\n#define ngx_quic_tp_vint(id, value)                                           \\\n    do {                                                                      \\\n        ngx_quic_build_int(&p, id);                                           \\\n        ngx_quic_build_int(&p, ngx_quic_varint_len(value));                   \\\n        ngx_quic_build_int(&p, value);                                        \\\n    } while (0)\n\n#define ngx_quic_tp_strlen(id, value)                                         \\\n    ngx_quic_varint_len(id)                                                   \\\n    + ngx_quic_varint_len(value.len)                                          \\\n    + value.len\n\n#define ngx_quic_tp_str(id, value)                                            \\\n    do {                                                                      \\\n        ngx_quic_build_int(&p, id);                                           \\\n        ngx_quic_build_int(&p, value.len);                                    \\\n        p = ngx_cpymem(p, value.data, value.len);                             \\\n    } while (0)\n\n    len = ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_DATA, tp->initial_max_data);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI,\n                           tp->initial_max_streams_uni);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI,\n                           tp->initial_max_streams_bidi);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,\n                           tp->initial_max_stream_data_bidi_local);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,\n                           tp->initial_max_stream_data_bidi_remote);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI,\n                           tp->initial_max_stream_data_uni);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_MAX_IDLE_TIMEOUT,\n                           tp->max_idle_timeout);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_MAX_UDP_PAYLOAD_SIZE,\n                           tp->max_udp_payload_size);\n\n    if (tp->disable_active_migration) {\n        len += ngx_quic_varint_len(NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION);\n        len += ngx_quic_varint_len(0);\n    }\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT,\n                           tp->active_connection_id_limit);\n\n    /* transport parameters listed above will be saved in 0-RTT context */\n    if (clen) {\n        *clen = len;\n    }\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_MAX_ACK_DELAY,\n                           tp->max_ack_delay);\n\n    len += ngx_quic_tp_len(NGX_QUIC_TP_ACK_DELAY_EXPONENT,\n                           tp->ack_delay_exponent);\n\n    len += ngx_quic_tp_strlen(NGX_QUIC_TP_ORIGINAL_DCID, tp->original_dcid);\n    len += ngx_quic_tp_strlen(NGX_QUIC_TP_INITIAL_SCID, tp->initial_scid);\n\n    if (tp->retry_scid.len) {\n        len += ngx_quic_tp_strlen(NGX_QUIC_TP_RETRY_SCID, tp->retry_scid);\n    }\n\n    len += ngx_quic_varint_len(NGX_QUIC_TP_SR_TOKEN);\n    len += ngx_quic_varint_len(NGX_QUIC_SR_TOKEN_LEN);\n    len += NGX_QUIC_SR_TOKEN_LEN;\n\n    if (pos == NULL) {\n        return len;\n    }\n\n    p = pos;\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_DATA,\n                     tp->initial_max_data);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI,\n                     tp->initial_max_streams_uni);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI,\n                     tp->initial_max_streams_bidi);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,\n                     tp->initial_max_stream_data_bidi_local);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,\n                     tp->initial_max_stream_data_bidi_remote);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI,\n                     tp->initial_max_stream_data_uni);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_MAX_IDLE_TIMEOUT,\n                     tp->max_idle_timeout);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_MAX_UDP_PAYLOAD_SIZE,\n                     tp->max_udp_payload_size);\n\n    if (tp->disable_active_migration) {\n        ngx_quic_build_int(&p, NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION);\n        ngx_quic_build_int(&p, 0);\n    }\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT,\n                     tp->active_connection_id_limit);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_MAX_ACK_DELAY,\n                     tp->max_ack_delay);\n\n    ngx_quic_tp_vint(NGX_QUIC_TP_ACK_DELAY_EXPONENT,\n                     tp->ack_delay_exponent);\n\n    ngx_quic_tp_str(NGX_QUIC_TP_ORIGINAL_DCID, tp->original_dcid);\n    ngx_quic_tp_str(NGX_QUIC_TP_INITIAL_SCID, tp->initial_scid);\n\n    if (tp->retry_scid.len) {\n        ngx_quic_tp_str(NGX_QUIC_TP_RETRY_SCID, tp->retry_scid);\n    }\n\n    ngx_quic_build_int(&p, NGX_QUIC_TP_SR_TOKEN);\n    ngx_quic_build_int(&p, NGX_QUIC_SR_TOKEN_LEN);\n    p = ngx_cpymem(p, tp->sr_token, NGX_QUIC_SR_TOKEN_LEN);\n\n    return p - pos;\n}\n\n\nstatic size_t\nngx_quic_create_close(u_char *p, ngx_quic_frame_t *f)\n{\n    size_t                   len;\n    u_char                  *start;\n    ngx_quic_close_frame_t  *cl;\n\n    cl = &f->u.close;\n\n    if (p == NULL) {\n        len = ngx_quic_varint_len(f->type);\n        len += ngx_quic_varint_len(cl->error_code);\n\n        if (f->type != NGX_QUIC_FT_CONNECTION_CLOSE_APP) {\n            len += ngx_quic_varint_len(cl->frame_type);\n        }\n\n        len += ngx_quic_varint_len(cl->reason.len);\n        len += cl->reason.len;\n\n        return len;\n    }\n\n    start = p;\n\n    ngx_quic_build_int(&p, f->type);\n    ngx_quic_build_int(&p, cl->error_code);\n\n    if (f->type != NGX_QUIC_FT_CONNECTION_CLOSE_APP) {\n        ngx_quic_build_int(&p, cl->frame_type);\n    }\n\n    ngx_quic_build_int(&p, cl->reason.len);\n    p = ngx_cpymem(p, cl->reason.data, cl->reason.len);\n\n    return p - start;\n}\n\n\nvoid\nngx_quic_dcid_encode_key(u_char *dcid, uint64_t key)\n{\n    (void) ngx_quic_write_uint64(dcid, key);\n}\n"
  },
  {
    "path": "src/event/quic/ngx_event_quic_transport.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_QUIC_TRANSPORT_H_INCLUDED_\n#define _NGX_EVENT_QUIC_TRANSPORT_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * RFC 9000, 17.2.  Long Header Packets\n *           17.3.  Short Header Packets\n *\n * QUIC flags in first byte\n */\n#define NGX_QUIC_PKT_LONG       0x80  /* header form */\n#define NGX_QUIC_PKT_FIXED_BIT  0x40\n#define NGX_QUIC_PKT_TYPE       0x30  /* in long packet */\n#define NGX_QUIC_PKT_KPHASE     0x04  /* in short packet */\n\n#define ngx_quic_long_pkt(flags)  ((flags) & NGX_QUIC_PKT_LONG)\n#define ngx_quic_short_pkt(flags)  (((flags) & NGX_QUIC_PKT_LONG) == 0)\n\n/* Long packet types */\n#define NGX_QUIC_PKT_INITIAL    0x00\n#define NGX_QUIC_PKT_ZRTT       0x10\n#define NGX_QUIC_PKT_HANDSHAKE  0x20\n#define NGX_QUIC_PKT_RETRY      0x30\n\n#define ngx_quic_pkt_in(flags)                                                \\\n    (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_INITIAL)\n#define ngx_quic_pkt_zrtt(flags)                                              \\\n    (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_ZRTT)\n#define ngx_quic_pkt_hs(flags)                                                \\\n    (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_HANDSHAKE)\n#define ngx_quic_pkt_retry(flags)                                             \\\n    (((flags) & NGX_QUIC_PKT_TYPE) == NGX_QUIC_PKT_RETRY)\n\n#define ngx_quic_pkt_rb_mask(flags)                                           \\\n    (ngx_quic_long_pkt(flags) ? 0x0C : 0x18)\n#define ngx_quic_pkt_hp_mask(flags)                                           \\\n    (ngx_quic_long_pkt(flags) ? 0x0F : 0x1F)\n\n#define ngx_quic_level_name(lvl)                                              \\\n    (lvl == ssl_encryption_application) ? \"app\"                               \\\n        : (lvl == ssl_encryption_initial) ? \"init\"                            \\\n            : (lvl == ssl_encryption_handshake) ? \"hs\" : \"early\"\n\n#define NGX_QUIC_MAX_CID_LEN                             20\n#define NGX_QUIC_SERVER_CID_LEN                          NGX_QUIC_MAX_CID_LEN\n\n/* 12.4.  Frames and Frame Types */\n#define NGX_QUIC_FT_PADDING                              0x00\n#define NGX_QUIC_FT_PING                                 0x01\n#define NGX_QUIC_FT_ACK                                  0x02\n#define NGX_QUIC_FT_ACK_ECN                              0x03\n#define NGX_QUIC_FT_RESET_STREAM                         0x04\n#define NGX_QUIC_FT_STOP_SENDING                         0x05\n#define NGX_QUIC_FT_CRYPTO                               0x06\n#define NGX_QUIC_FT_NEW_TOKEN                            0x07\n#define NGX_QUIC_FT_STREAM                               0x08\n#define NGX_QUIC_FT_STREAM1                              0x09\n#define NGX_QUIC_FT_STREAM2                              0x0A\n#define NGX_QUIC_FT_STREAM3                              0x0B\n#define NGX_QUIC_FT_STREAM4                              0x0C\n#define NGX_QUIC_FT_STREAM5                              0x0D\n#define NGX_QUIC_FT_STREAM6                              0x0E\n#define NGX_QUIC_FT_STREAM7                              0x0F\n#define NGX_QUIC_FT_MAX_DATA                             0x10\n#define NGX_QUIC_FT_MAX_STREAM_DATA                      0x11\n#define NGX_QUIC_FT_MAX_STREAMS                          0x12\n#define NGX_QUIC_FT_MAX_STREAMS2                         0x13\n#define NGX_QUIC_FT_DATA_BLOCKED                         0x14\n#define NGX_QUIC_FT_STREAM_DATA_BLOCKED                  0x15\n#define NGX_QUIC_FT_STREAMS_BLOCKED                      0x16\n#define NGX_QUIC_FT_STREAMS_BLOCKED2                     0x17\n#define NGX_QUIC_FT_NEW_CONNECTION_ID                    0x18\n#define NGX_QUIC_FT_RETIRE_CONNECTION_ID                 0x19\n#define NGX_QUIC_FT_PATH_CHALLENGE                       0x1A\n#define NGX_QUIC_FT_PATH_RESPONSE                        0x1B\n#define NGX_QUIC_FT_CONNECTION_CLOSE                     0x1C\n#define NGX_QUIC_FT_CONNECTION_CLOSE_APP                 0x1D\n#define NGX_QUIC_FT_HANDSHAKE_DONE                       0x1E\n\n#define NGX_QUIC_FT_LAST  NGX_QUIC_FT_HANDSHAKE_DONE\n\n/* 22.5.  QUIC Transport Error Codes Registry */\n#define NGX_QUIC_ERR_NO_ERROR                            0x00\n#define NGX_QUIC_ERR_INTERNAL_ERROR                      0x01\n#define NGX_QUIC_ERR_CONNECTION_REFUSED                  0x02\n#define NGX_QUIC_ERR_FLOW_CONTROL_ERROR                  0x03\n#define NGX_QUIC_ERR_STREAM_LIMIT_ERROR                  0x04\n#define NGX_QUIC_ERR_STREAM_STATE_ERROR                  0x05\n#define NGX_QUIC_ERR_FINAL_SIZE_ERROR                    0x06\n#define NGX_QUIC_ERR_FRAME_ENCODING_ERROR                0x07\n#define NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR           0x08\n#define NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR           0x09\n#define NGX_QUIC_ERR_PROTOCOL_VIOLATION                  0x0A\n#define NGX_QUIC_ERR_INVALID_TOKEN                       0x0B\n#define NGX_QUIC_ERR_APPLICATION_ERROR                   0x0C\n#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED              0x0D\n#define NGX_QUIC_ERR_KEY_UPDATE_ERROR                    0x0E\n#define NGX_QUIC_ERR_AEAD_LIMIT_REACHED                  0x0F\n#define NGX_QUIC_ERR_NO_VIABLE_PATH                      0x10\n\n#define NGX_QUIC_ERR_CRYPTO_ERROR                       0x100\n\n#define NGX_QUIC_ERR_CRYPTO(e)  (NGX_QUIC_ERR_CRYPTO_ERROR + (e))\n\n\n/* 22.3.  QUIC Transport Parameters Registry */\n#define NGX_QUIC_TP_ORIGINAL_DCID                        0x00\n#define NGX_QUIC_TP_MAX_IDLE_TIMEOUT                     0x01\n#define NGX_QUIC_TP_SR_TOKEN                             0x02\n#define NGX_QUIC_TP_MAX_UDP_PAYLOAD_SIZE                 0x03\n#define NGX_QUIC_TP_INITIAL_MAX_DATA                     0x04\n#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL   0x05\n#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE  0x06\n#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI          0x07\n#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI             0x08\n#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI              0x09\n#define NGX_QUIC_TP_ACK_DELAY_EXPONENT                   0x0A\n#define NGX_QUIC_TP_MAX_ACK_DELAY                        0x0B\n#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION             0x0C\n#define NGX_QUIC_TP_PREFERRED_ADDRESS                    0x0D\n#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT           0x0E\n#define NGX_QUIC_TP_INITIAL_SCID                         0x0F\n#define NGX_QUIC_TP_RETRY_SCID                           0x10\n\n#define NGX_QUIC_CID_LEN_MIN                                8\n#define NGX_QUIC_CID_LEN_MAX                               20\n\n#define NGX_QUIC_MAX_RANGES                                10\n\n\ntypedef struct {\n    uint64_t                                    gap;\n    uint64_t                                    range;\n} ngx_quic_ack_range_t;\n\n\ntypedef struct {\n    uint64_t                                    largest;\n    uint64_t                                    delay;\n    uint64_t                                    range_count;\n    uint64_t                                    first_range;\n    uint64_t                                    ect0;\n    uint64_t                                    ect1;\n    uint64_t                                    ce;\n    uint64_t                                    ranges_length;\n} ngx_quic_ack_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    seqnum;\n    uint64_t                                    retire;\n    uint8_t                                     len;\n    u_char                                      cid[NGX_QUIC_CID_LEN_MAX];\n    u_char                                      srt[NGX_QUIC_SR_TOKEN_LEN];\n} ngx_quic_new_conn_id_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    length;\n    u_char                                     *data;\n} ngx_quic_new_token_frame_t;\n\n/*\n * common layout for CRYPTO and STREAM frames;\n * conceptually, CRYPTO frame is also a stream\n * frame lacking some properties\n */\ntypedef struct {\n    uint64_t                                    offset;\n    uint64_t                                    length;\n} ngx_quic_ordered_frame_t;\n\ntypedef ngx_quic_ordered_frame_t  ngx_quic_crypto_frame_t;\n\n\ntypedef struct {\n    /* initial fields same as in ngx_quic_ordered_frame_t */\n    uint64_t                                    offset;\n    uint64_t                                    length;\n\n    uint64_t                                    stream_id;\n    unsigned                                    off:1;\n    unsigned                                    len:1;\n    unsigned                                    fin:1;\n} ngx_quic_stream_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    max_data;\n} ngx_quic_max_data_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    error_code;\n    uint64_t                                    frame_type;\n    ngx_str_t                                   reason;\n} ngx_quic_close_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    id;\n    uint64_t                                    error_code;\n    uint64_t                                    final_size;\n} ngx_quic_reset_stream_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    id;\n    uint64_t                                    error_code;\n} ngx_quic_stop_sending_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    limit;\n    ngx_uint_t                                  bidi;  /* unsigned: bidi:1 */\n} ngx_quic_streams_blocked_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    limit;\n    ngx_uint_t                                  bidi;  /* unsigned: bidi:1 */\n} ngx_quic_max_streams_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    id;\n    uint64_t                                    limit;\n} ngx_quic_max_stream_data_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    limit;\n} ngx_quic_data_blocked_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    id;\n    uint64_t                                    limit;\n} ngx_quic_stream_data_blocked_frame_t;\n\n\ntypedef struct {\n    uint64_t                                    sequence_number;\n} ngx_quic_retire_cid_frame_t;\n\n\ntypedef struct {\n    u_char                                      data[8];\n} ngx_quic_path_challenge_frame_t;\n\n\ntypedef struct ngx_quic_frame_s                 ngx_quic_frame_t;\n\nstruct ngx_quic_frame_s {\n    ngx_uint_t                                  type;\n    enum ssl_encryption_level_t                 level;\n    ngx_queue_t                                 queue;\n    uint64_t                                    pnum;\n    size_t                                      plen;\n    ngx_msec_t                                  first;\n    ngx_msec_t                                  last;\n    ssize_t                                     len;\n    unsigned                                    need_ack:1;\n    unsigned                                    pkt_need_ack:1;\n    unsigned                                    flush:1;\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    unsigned                                    probe:1;\n#endif\n\n    ngx_chain_t                                *data;\n    union {\n        ngx_quic_ack_frame_t                    ack;\n        ngx_quic_crypto_frame_t                 crypto;\n        ngx_quic_ordered_frame_t                ord;\n        ngx_quic_new_conn_id_frame_t            ncid;\n        ngx_quic_new_token_frame_t              token;\n        ngx_quic_stream_frame_t                 stream;\n        ngx_quic_max_data_frame_t               max_data;\n        ngx_quic_close_frame_t                  close;\n        ngx_quic_reset_stream_frame_t           reset_stream;\n        ngx_quic_stop_sending_frame_t           stop_sending;\n        ngx_quic_streams_blocked_frame_t        streams_blocked;\n        ngx_quic_max_streams_frame_t            max_streams;\n        ngx_quic_max_stream_data_frame_t        max_stream_data;\n        ngx_quic_data_blocked_frame_t           data_blocked;\n        ngx_quic_stream_data_blocked_frame_t    stream_data_blocked;\n        ngx_quic_retire_cid_frame_t             retire_cid;\n        ngx_quic_path_challenge_frame_t         path_challenge;\n        ngx_quic_path_challenge_frame_t         path_response;\n    } u;\n};\n\n\ntypedef struct {\n    ngx_log_t                                  *log;\n\n    ngx_quic_keys_t                            *keys;\n\n    ngx_msec_t                                  received;\n    uint64_t                                    number;\n    uint8_t                                     num_len;\n    uint32_t                                    trunc;\n    uint8_t                                     flags;\n    uint32_t                                    version;\n    ngx_str_t                                   token;\n    enum ssl_encryption_level_t                 level;\n    ngx_uint_t                                  error;\n\n    /* filled in by parser */\n    ngx_buf_t                                  *raw;   /* udp datagram */\n\n    u_char                                     *data;  /* quic packet */\n    size_t                                      len;\n\n    /* cleartext fields */\n    ngx_str_t                                   odcid; /* retry packet tag */\n    ngx_str_t                                   dcid;\n    ngx_str_t                                   scid;\n    uint64_t                                    pn;\n    u_char                                     *plaintext;\n    ngx_str_t                                   payload; /* decrypted data */\n\n    unsigned                                    need_ack:1;\n    unsigned                                    key_phase:1;\n    unsigned                                    key_update:1;\n    unsigned                                    parsed:1;\n    unsigned                                    decrypted:1;\n    unsigned                                    validated:1;\n    unsigned                                    retried:1;\n    unsigned                                    first:1;\n} ngx_quic_header_t;\n\n\nngx_int_t ngx_quic_parse_packet(ngx_quic_header_t *pkt);\n\nsize_t ngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out);\n\nsize_t ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len);\n\nsize_t ngx_quic_create_header(ngx_quic_header_t *pkt, u_char *out,\n    u_char **pnp);\n\nsize_t ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out,\n    u_char **start);\n\nssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,\n    ngx_quic_frame_t *frame);\nssize_t ngx_quic_create_frame(u_char *p, ngx_quic_frame_t *f);\n\nssize_t ngx_quic_parse_ack_range(ngx_log_t *log, u_char *start,\n    u_char *end, uint64_t *gap, uint64_t *range);\nsize_t ngx_quic_create_ack_range(u_char *p, uint64_t gap, uint64_t range);\n\nngx_int_t ngx_quic_parse_transport_params(u_char *p, u_char *end,\n    ngx_quic_tp_t *tp, ngx_log_t *log);\nssize_t ngx_quic_create_transport_params(u_char *p, u_char *end,\n    ngx_quic_tp_t *tp, size_t *clen);\n\nvoid ngx_quic_dcid_encode_key(u_char *dcid, uint64_t key);\n\n#endif /* _NGX_EVENT_QUIC_TRANSPORT_H_INCLUDED_ */\n"
  },
  {
    "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    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;\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            if (h) {\n                ho = ngx_list_push(&r->headers_out.headers);\n                if (ho == NULL) {\n                    return NGX_ERROR;\n                }\n\n                *ho = *h;\n\n                r->headers_out.www_authenticate = ho;\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    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 (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    { 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      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    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, *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, **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 (params->number) {\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            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 += sizeof(\"HTTP_\") - 1;\n\n            } else {\n                n = sizeof(\"HTTP_\") - 1 + header[i].key.len;\n            }\n\n            len += ((n > 127) ? 4 : 1) + ((header[i].value.len > 127) ? 4 : 1)\n                + n + header[i].value.len;\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;\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            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, val_len);\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:\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 && hh->handler(r, h, hh->offset) != NGX_OK) {\n                    return NGX_ERROR;\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    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\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\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_array_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->nelts > 0 && 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    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);\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);\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    { 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    { 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_array_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->nelts > 0 && 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_array_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->nelts > 0 && 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\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\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 (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_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#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->upstream.ssl != NULL);\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 && hh->handler(r, h, hh->offset) != NGX_OK) {\n                    return NGX_ERROR;\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_v2_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_v2_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#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    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|NGX_SSL_TLSv1\n                                  |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));\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#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->upstream.ssl = prev->upstream.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_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    glcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (glcf->upstream.ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    glcf->upstream.ssl->log = cf->log;\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\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 (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                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\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\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\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      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    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    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 forces window bits to 13 for fast compression level,\n         * uses 16-byte padding in one of window-sized buffers, and\n         * uses 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        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\n        /*\n         * The zlib deflate_state allocation, it takes about 6K,\n         * we allocate 8K.  Other allocations are divisible by 512.\n         */\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\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\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    ngx_str_set(&h->key, \"Content-Encoding\");\n    ngx_str_set(&h->value, \"gzip\");\n    r->headers_out.content_encoding = h;\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\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_uint_t           i;\n    ngx_table_elt_t     *e, *cc, **ccp;\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\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    ccp = r->headers_out.cache_control.elts;\n\n    if (ccp == NULL) {\n\n        if (ngx_array_init(&r->headers_out.cache_control, r->pool,\n                           1, sizeof(ngx_table_elt_t *))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        cc = ngx_list_push(&r->headers_out.headers);\n        if (cc == NULL) {\n            return NGX_ERROR;\n        }\n\n        cc->hash = 1;\n        ngx_str_set(&cc->key, \"Cache-Control\");\n\n        ccp = ngx_array_push(&r->headers_out.cache_control);\n        if (ccp == NULL) {\n            return NGX_ERROR;\n        }\n\n        *ccp = cc;\n\n    } else {\n        for (i = 1; i < r->headers_out.cache_control.nelts; i++) {\n            ccp[i]->hash = 0;\n        }\n\n        cc = ccp[0];\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        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        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_array_t      *pa;\n    ngx_table_elt_t  *h, **ph;\n\n    if (value->len == 0) {\n        return NGX_OK;\n    }\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, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\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_array_push(pa);\n    if (ph == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ph = h;\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    }\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\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\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    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    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    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);\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      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    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 ((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 (conf->filter == NGX_HTTP_IMAGE_CROP) {\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            ox /= 2;\n            oy /= 2;\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    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    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    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    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    } 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\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\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} 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} ngx_http_limit_req_limit_t;\n\n\ntypedef struct {\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);\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      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,\n      ngx_http_limit_req_zone,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_req\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\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      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\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    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 (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        r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED;\n\n        return lrcf->status_code;\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    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\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\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    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    rate = 1;\n    scale = 1;\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        if (ngx_strncmp(value[i].data, \"rate=\", 5) == 0) {\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        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    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\n    value = cf->args->elts;\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        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\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\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);\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    { 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 (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    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    return ngx_sprintf(buf, \"%T.%03M\", (time_t) ms / 1000, ms % 1000);\n}\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    }\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      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            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\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    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    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    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    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    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    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    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    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    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    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    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    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    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    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    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 -= 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    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    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    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    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    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    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\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_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    { 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    { 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#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->upstream.ssl != NULL);\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    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 && hh->handler(r, h, hh->offset) != NGX_OK) {\n                return NGX_ERROR;\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            }\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            }\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_uint_t         i, n;\n    ngx_table_elt_t  **h;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    n = r->headers_in.x_forwarded_for.nelts;\n    h = r->headers_in.x_forwarded_for.elts;\n\n    len = 0;\n\n    for (i = 0; i < n; i++) {\n        len += h[i]->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 (i = 0; i < n; i++) {\n        p = ngx_copy(p, h[i]->value.data, h[i]->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#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    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    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    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|NGX_SSL_TLSv1\n                                  |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));\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#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->upstream.ssl = prev->upstream.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_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (plcf->upstream.ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    plcf->upstream.ssl->log = cf->log;\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\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 (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                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\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_quic_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Roman Arutyunyan\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_variable_quic(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#if (NGX_HAVE_IP_MTU_DISCOVER)\nstatic ngx_int_t ngx_http_variable_quic_mtu(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\nstatic ngx_int_t ngx_http_quic_add_variables(ngx_conf_t *cf);\nstatic void *ngx_http_quic_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post,\n    void *data);\nstatic char *ngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post,\n    void *data);\nstatic char *ngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic ngx_conf_post_t  ngx_http_quic_max_ack_delay_post =\n    { ngx_http_quic_max_ack_delay };\nstatic ngx_conf_post_t  ngx_http_quic_max_udp_payload_size_post =\n    { ngx_http_quic_max_udp_payload_size };\nstatic ngx_conf_num_bounds_t  ngx_http_quic_ack_delay_exponent_bounds =\n    { ngx_conf_check_num_bounds, 0, 20 };\nstatic ngx_conf_num_bounds_t  ngx_http_quic_active_connection_id_limit_bounds =\n    { ngx_conf_check_num_bounds, 2, -1 };\n\n\nstatic ngx_command_t  ngx_http_quic_commands[] = {\n\n    { ngx_string(\"quic_max_idle_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_quic_conf_t, tp.max_idle_timeout),\n      NULL },\n\n    { ngx_string(\"quic_max_ack_delay\"),\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_quic_conf_t, tp.max_ack_delay),\n      &ngx_http_quic_max_ack_delay_post },\n\n    { ngx_string(\"quic_max_udp_payload_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_quic_conf_t, tp.max_udp_payload_size),\n      &ngx_http_quic_max_udp_payload_size_post },\n\n    { ngx_string(\"quic_initial_max_data\"),\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_quic_conf_t, tp.initial_max_data),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_stream_data_bidi_local\"),\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_quic_conf_t, tp.initial_max_stream_data_bidi_local),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_stream_data_bidi_remote\"),\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_quic_conf_t, tp.initial_max_stream_data_bidi_remote),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_stream_data_uni\"),\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_quic_conf_t, tp.initial_max_stream_data_uni),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_streams_bidi\"),\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_quic_conf_t, tp.initial_max_streams_bidi),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_streams_uni\"),\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_quic_conf_t, tp.initial_max_streams_uni),\n      NULL },\n\n    { ngx_string(\"quic_ack_delay_exponent\"),\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_quic_conf_t, tp.ack_delay_exponent),\n      &ngx_http_quic_ack_delay_exponent_bounds },\n\n    { ngx_string(\"quic_disable_active_migration\"),\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_quic_conf_t, tp.disable_active_migration),\n      NULL },\n\n    { ngx_string(\"quic_active_connection_id_limit\"),\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_quic_conf_t, tp.active_connection_id_limit),\n      &ngx_http_quic_active_connection_id_limit_bounds },\n\n    { ngx_string(\"quic_stream_buf_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_quic_conf_t, stream_buf_size),\n      NULL },\n\n    { ngx_string(\"quic_stream_shuffle\"),\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_quic_conf_t, stream_shuffle),\n      NULL },\n\n    { ngx_string(\"quic_initial_window\"),\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_quic_conf_t, initial_window),\n      NULL },\n\n    { ngx_string(\"quic_min_window\"),\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_quic_conf_t, min_window),\n      NULL },\n\n    { ngx_string(\"quic_retry\"),\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_quic_conf_t, retry),\n      NULL },\n\n    { ngx_string(\"quic_gso\"),\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_quic_conf_t, gso_enabled),\n      NULL },\n\n    { ngx_string(\"quic_nodelay\"),\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_quic_conf_t, nodelay),\n      NULL },\n\n     { ngx_string(\"quic_migration_close_connection\"),\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_quic_conf_t, migration_close_connection),\n      NULL },\n\n    { ngx_string(\"quic_host_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_quic_host_key,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    { ngx_string(\"quic_mtu_discovery\"),\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_quic_conf_t, mtu),\n      NULL },\n\n    { ngx_string(\"quic_mtu_discovery_attemts\"),\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_quic_conf_t, mtu_attemts),\n      NULL },\n\n    { ngx_string(\"quic_mtu_discovery_target\"),\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_quic_conf_t, mtu_target),\n      NULL },\n#endif\n\n#if (NGX_HAVE_UDP_SENDMMSG)\n    { ngx_string(\"quic_sendmmsg\"),\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_quic_conf_t, sendmmsg_enabled),\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_quic_module_ctx = {\n    ngx_http_quic_add_variables,           /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_quic_create_srv_conf,         /* create server configuration */\n    ngx_http_quic_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_quic_module = {\n    NGX_MODULE_V1,\n    &ngx_http_quic_module_ctx,             /* module context */\n    ngx_http_quic_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_quic_vars[] = {\n\n    { ngx_string(\"quic\"), NULL, ngx_http_variable_quic, 0, 0, 0 },\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    { ngx_string(\"quic_mtu\"), NULL, ngx_http_variable_quic_mtu, 0, 0, 0 },\n#endif\n\n      ngx_http_null_variable\n};\n\nstatic ngx_str_t  ngx_http_quic_salt = ngx_string(\"ngx_quic\");\n\n\nngx_int_t\nngx_http_quic_init(ngx_connection_t *c)\n{\n    uint64_t                   n;\n    ngx_quic_conf_t           *qcf;\n    ngx_http_connection_t     *hc, *phc;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    hc = c->data;\n\n    hc->ssl = 1;\n\n    if (c->quic == NULL) {\n        qcf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_quic_module);\n\n        ngx_quic_run(c, qcf);\n\n        return NGX_DONE;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http init quic stream\");\n\n#if (NGX_HTTP_V3)\n    if (!hc->addr_conf->http3)\n#endif\n    {\n        /* Use HTTP/3 General Protocol Error Code 0x101 for finalization */\n\n        if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {\n            ngx_quic_finalize_connection(c->quic->parent, NGX_ERROR, 0x101,\n                                         \"unexpected uni stream\");\n            ngx_http_close_connection(c);\n            return NGX_DONE;\n        }\n\n        clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);\n\n        n = c->quic->id >> 2;\n\n        if (n >= clcf->keepalive_requests) {\n            ngx_quic_finalize_connection(c->quic->parent, NGX_ERROR, 0x101,\n                                         \"reached maximum number of requests\");\n            ngx_http_close_connection(c);\n            return NGX_DONE;\n        }\n\n        if (ngx_current_msec - c->quic->parent->start_time\n            > clcf->keepalive_time)\n        {\n            ngx_quic_finalize_connection(c->quic->parent, NGX_ERROR, 0x101,\n                                          \"reached maximum time for requests\");\n            ngx_http_close_connection(c);\n            return NGX_DONE;\n        }\n    }\n\n    phc = ngx_http_quic_get_connection(c);\n\n    if (phc->ssl_servername) {\n        hc->ssl_servername = phc->ssl_servername;\n        hc->conf_ctx = phc->conf_ctx;\n\n        clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);\n        ngx_set_connection_log(c, clcf->error_log);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_quic(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->connection->quic) {\n\n        v->len = 4;\n        v->valid = 1;\n        v->no_cacheable = 1;\n        v->not_found = 0;\n        v->data = (u_char *) \"quic\";\n        return NGX_OK;\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\nstatic ngx_int_t\nngx_http_variable_quic_mtu(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t  mtu;\n\n    if (r->connection->quic) {\n        mtu = ngx_quic_mtu(r->connection);\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, \"%z\", mtu) - v->data;\n\n        v->valid = 1;\n        v->no_cacheable = 1;\n        v->not_found = 0;\n\n        return NGX_OK;\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_http_quic_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_quic_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_quic_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_quic_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_quic_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->tp.original_dcid = { 0, NULL };\n     *     conf->tp.initial_scid = { 0, NULL };\n     *     conf->tp.retry_scid = { 0, NULL };\n     *     conf->tp.sr_token = { 0 }\n     *     conf->tp.sr_enabled = 0\n     *     conf->tp.preferred_address = NULL\n     *     conf->host_key = { 0, NULL }\n     *     cong->stream_reject_code_uni = 0;\n     */\n\n    conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC;\n    conf->tp.max_ack_delay = NGX_CONF_UNSET_MSEC;\n    conf->tp.max_udp_payload_size = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_data = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT;\n    conf->tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT;\n    conf->tp.ack_delay_exponent = NGX_CONF_UNSET_UINT;\n    conf->tp.disable_active_migration = NGX_CONF_UNSET;\n    conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT;\n\n    conf->stream_buf_size = NGX_CONF_UNSET_SIZE;\n    conf->initial_window = NGX_CONF_UNSET_SIZE;\n    conf->min_window = NGX_CONF_UNSET_SIZE;\n    conf->retry = NGX_CONF_UNSET;\n    conf->gso_enabled = NGX_CONF_UNSET;\n#if (NGX_HTTP_V3)\n    conf->stream_close_code = NGX_HTTP_V3_ERR_NO_ERROR;\n    conf->stream_reject_code_bidi = NGX_HTTP_V3_ERR_REQUEST_REJECTED;\n#endif\n    conf->migration_close_connection = NGX_CONF_UNSET;\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    conf->mtu = NGX_CONF_UNSET;\n    conf->mtu_attemts = NGX_CONF_UNSET;\n    conf->mtu_target = NGX_CONF_UNSET_SIZE;\n#endif\n\n    conf->stream_shuffle = NGX_CONF_UNSET_SIZE;\n    conf->nodelay = NGX_CONF_UNSET;\n\n#if (NGX_HAVE_UDP_SENDMMSG)\n    conf->sendmmsg_enabled = NGX_CONF_UNSET;\n#endif\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_quic_conf_t *prev = parent;\n    ngx_quic_conf_t *conf = child;\n\n    ngx_http_ssl_srv_conf_t  *sscf;\n\n    ngx_conf_merge_size_value(conf->stream_buf_size,\n                              prev->stream_buf_size,\n                              NGX_QUIC_STREAM_BUFSIZE);\n\n    ngx_conf_merge_msec_value(conf->tp.max_idle_timeout,\n                              prev->tp.max_idle_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->tp.max_ack_delay,\n                              prev->tp.max_ack_delay,\n                              NGX_QUIC_DEFAULT_MAX_ACK_DELAY);\n\n    ngx_conf_merge_size_value(conf->tp.max_udp_payload_size,\n                              prev->tp.max_udp_payload_size,\n                              NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_data,\n                              prev->tp.initial_max_data,\n                              16 * conf->stream_buf_size);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_local,\n                              prev->tp.initial_max_stream_data_bidi_local,\n                              conf->stream_buf_size);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_remote,\n                              prev->tp.initial_max_stream_data_bidi_remote,\n                              conf->stream_buf_size);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_uni,\n                              prev->tp.initial_max_stream_data_uni,\n                              conf->stream_buf_size);\n\n    ngx_conf_merge_uint_value(conf->tp.initial_max_streams_bidi,\n                              prev->tp.initial_max_streams_bidi, 16);\n\n    ngx_conf_merge_uint_value(conf->tp.initial_max_streams_uni,\n                              prev->tp.initial_max_streams_uni, 3);\n\n    ngx_conf_merge_uint_value(conf->tp.ack_delay_exponent,\n                              prev->tp.ack_delay_exponent,\n                              NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT);\n\n    ngx_conf_merge_value(conf->tp.disable_active_migration,\n                              prev->tp.disable_active_migration, 0);\n\n    ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit,\n                              prev->tp.active_connection_id_limit, 2);\n\n    ngx_conf_merge_size_value(conf->initial_window, prev->initial_window, 0);\n    ngx_conf_merge_size_value(conf->min_window, prev->min_window, 0);\n\n    ngx_conf_merge_value(conf->retry, prev->retry, 0);\n    ngx_conf_merge_value(conf->gso_enabled, prev->gso_enabled, 0);\n    ngx_conf_merge_value(conf->migration_close_connection, prev->migration_close_connection, 0);\n\n    ngx_conf_merge_str_value(conf->host_key, prev->host_key, \"\");\n\n#if (NGX_HAVE_IP_MTU_DISCOVER)\n    ngx_conf_merge_value(conf->mtu, prev->mtu, 0);\n    ngx_conf_merge_size_value(conf->mtu_target, prev->mtu_target, 1472);\n    ngx_conf_merge_value(conf->mtu_attemts, prev->mtu_attemts, 8);\n#endif\n\n    ngx_conf_merge_value(conf->nodelay, prev->nodelay, 0);\n\n    ngx_conf_merge_uint_value(conf->stream_shuffle, prev->stream_shuffle, 8);\n\n#if (NGX_HAVE_UDP_SENDMMSG)\n    ngx_conf_merge_value(conf->sendmmsg_enabled, prev->sendmmsg_enabled, 1);\n#endif\n\n    if (conf->migration_close_connection && !conf->tp.disable_active_migration) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"quic_migration_close_connection\\\" required enable \\\"quic_disable_active_migration\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->host_key.len == 0) {\n\n        conf->host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN;\n        conf->host_key.data = ngx_palloc(cf->pool, conf->host_key.len);\n        if (conf->host_key.data == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (RAND_bytes(conf->host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN)\n            <= 0)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_quic_derive_key(cf->log, \"av_token_key\",\n                            &conf->host_key, &ngx_http_quic_salt,\n                            conf->av_token_key, NGX_QUIC_AV_KEY_LEN)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_quic_derive_key(cf->log, \"sr_token_key\",\n                            &conf->host_key, &ngx_http_quic_salt,\n                            conf->sr_token_key, NGX_QUIC_SR_KEY_LEN)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module);\n    conf->ssl = &sscf->ssl;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_msec_t *sp = data;\n\n    if (*sp >= 16384) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"quic_max_ack_delay\\\" must be less than 16384\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *sp = data;\n\n    if (*sp < NGX_QUIC_MIN_INITIAL_SIZE\n        || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"quic_max_udp_payload_size\\\" must be between \"\n                           \"%d and %d\",\n                           NGX_QUIC_MIN_INITIAL_SIZE,\n                           NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_quic_conf_t  *qcf = conf;\n\n    u_char           *buf;\n    size_t            size;\n    ssize_t           n;\n    ngx_str_t        *value;\n    ngx_file_t        file;\n    ngx_file_info_t   fi;\n\n    if (qcf->host_key.len) {\n        return \"is duplicate\";\n    }\n\n    buf = NULL;\n#if (NGX_SUPPRESS_WARN)\n    size = 0;\n#endif\n\n    value = cf->args->elts;\n\n    if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n    file.name = value[1];\n    file.log = cf->log;\n\n    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 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_CONF_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 == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" zero key size\", &file.name);\n        goto failed;\n    }\n\n    buf = ngx_pnalloc(cf->pool, size);\n    if (buf == NULL) {\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    qcf->host_key.data = buf;\n    qcf->host_key.len = 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    return NGX_CONF_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    if (buf) {\n        ngx_explicit_memzero(buf, size);\n    }\n\n    return NGX_CONF_ERROR;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_quic_module.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Roman Arutyunyan\n */\n\n\n#ifndef _NGX_HTTP_QUIC_H_INCLUDED_\n#define _NGX_HTTP_QUIC_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_QUIC_ALPN_PROTO      \"\\x0Ahq-interop\"\n#define NGX_HTTP_QUIC_ALPN_DRAFT_FMT  \"\\x05hq-%02uD\"\n\n\n#define ngx_http_quic_get_connection(c)                                       \\\n    ((ngx_http_connection_t *) (c)->quic->parent->data)\n\n\nngx_int_t ngx_http_quic_init(ngx_connection_t *c);\n\n\n#endif /* _NGX_HTTP_QUIC_H_INCLUDED_ */\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    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    r->headers_out.content_range = content_range;\n\n    content_range->hash = 1;\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    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    r->headers_out.content_range = content_range;\n\n    content_range->hash = 1;\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_array_t                 *xfwd;\n    ngx_list_part_t             *part;\n    ngx_table_elt_t             *header;\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->elts == 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 (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    { 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, *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, **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 (params->number) {\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            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    }\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;\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            *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:\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 && hh->handler(r, h, hh->offset) != NGX_OK) {\n                return NGX_ERROR;\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;\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    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        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\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        || (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                  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\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);\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);\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    { 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    { 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    { 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    NULL,                                  /* create location configuration */\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_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      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#if (NGX_HTTP_QUIC)\n    const char             *fmt;\n#endif\n    unsigned int            srvlen;\n    unsigned char          *srv;\n#if (NGX_DEBUG)\n    unsigned int            i;\n#endif\n#if (NGX_HTTP_V2 || NGX_HTTP_QUIC)\n    ngx_http_connection_t  *hc;\n#endif\n#if (NGX_HTTP_V2 || NGX_HTTP_QUIC || 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 || NGX_HTTP_QUIC)\n    hc = c->data;\n#endif\n\n#if (NGX_HTTP_V2)\n    if (hc->addr_conf->http2) {\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#if (NGX_HTTP_QUIC)\n    if (hc->addr_conf->quic) {\n#if (NGX_HTTP_V3)\n        if (hc->addr_conf->http3) {\n            srv = (unsigned char *) NGX_HTTP_V3_ALPN_PROTO;\n            srvlen = sizeof(NGX_HTTP_V3_ALPN_PROTO) - 1;\n            fmt = NGX_HTTP_V3_ALPN_DRAFT_FMT;\n\n        } else\n#endif\n        {\n            srv = (unsigned char *) NGX_HTTP_QUIC_ALPN_PROTO;\n            srvlen = sizeof(NGX_HTTP_QUIC_ALPN_PROTO) - 1;\n            fmt = NGX_HTTP_QUIC_ALPN_DRAFT_FMT;\n        }\n\n        /* QUIC draft */\n\n        if (ngx_quic_version(c) > 1) {\n            srv = ngx_pnalloc(c->pool, sizeof(\"\\x05h3-xx\") - 1);\n            if (srv == NULL) {\n                return SSL_TLSEXT_ERR_NOACK;\n            }\n            srvlen = ngx_sprintf(srv, fmt, ngx_quic_version(c)) - srv;\n        }\n\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    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\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    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|NGX_SSL_TLSv1\n                          |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));\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    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\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\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\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#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\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\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) {\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) {\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    const char                  *name;\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 && !addr[a].opt.quic) {\n                continue;\n            }\n\n            if (addr[a].opt.http3) {\n                name = \"http3\";\n\n            } else if (addr[a].opt.quic) {\n                name = \"quic\";\n\n            } else {\n                name = \"ssl\";\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\n                if (addr[a].opt.quic && !(sscf->protocols & NGX_SSL_TLSv1_3)) {\n                    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                                  \"\\\"ssl_protocols\\\" must enable TLSv1.3 for \"\n                                  \"the \\\"listen ... %s\\\" directive in %s:%ui\",\n                                  name, cscf->file_name, cscf->line);\n                    return NGX_ERROR;\n                }\n\n                continue;\n            }\n\n            if (!sscf->reject_handshake) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_certificate\\\" is defined for \"\n                              \"the \\\"listen ... %s\\\" directive in %s:%ui\",\n                              name, 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                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_certificate\\\" is defined for \"\n                              \"the \\\"listen ... %s\\\" directive in %s:%ui\",\n                              name, 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    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} ngx_http_ssl_srv_conf_t;\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        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    if (r != r->main && of.size == 0) {\n        return ngx_http_send_header(r);\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\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\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    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_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 (!(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           + sizeof(\"server accepts handled requests\\n\") - 1\n           + 6 + 3 * NGX_ATOMIC_T_LEN\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\n    b->last = ngx_sprintf(b->last, \"Active connections: %uA \\n\", ac);\n\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\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"
  },
  {
    "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\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 (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\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 (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\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\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 (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 (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_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_int_t                n;\n    ngx_str_t                src, dst;\n    ngx_table_elt_t        **cookies;\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    n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,\n                                          &ctx->cookie);\n    if (n == NGX_DECLINED) {\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        cookies = r->headers_in.cookies.elts;\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent too short userid cookie \\\"%V\\\"\",\n                      &cookies[n]->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        cookies = r->headers_in.cookies.elts;\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent invalid userid cookie \\\"%V\\\"\",\n                      &cookies[n]->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_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    { 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#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->upstream.ssl != NULL);\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, *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, **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 (params->number) {\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            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    }\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;\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            *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, val_len);\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:\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 && hh->handler(r, h, hh->offset) != NGX_OK) {\n                return NGX_ERROR;\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#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    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|NGX_SSL_TLSv1\n                                  |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));\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#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->upstream.ssl = prev->upstream.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_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    uwcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (uwcf->upstream.ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    uwcf->upstream.ssl->log = cf->log;\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\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 (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                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\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, n, hash;\n    ngx_array_t                *a;\n    ngx_list_part_t            *part;\n    ngx_table_elt_t            *h, **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, cookies)) {\n            sep = ';';\n            goto multi;\n        }\n#if (NGX_HTTP_X_FORWARDED_FOR)\n        if (hh->offset == offsetof(ngx_http_headers_in_t, x_forwarded_for)) {\n            sep = ',';\n            goto multi;\n        }\n#endif\n\n        ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset);\n\n        if (*ph) {\n            ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len);\n\n            goto done;\n        }\n\n        XSRETURN_UNDEF;\n\n    multi:\n\n        /* Cookie, X-Forwarded-For */\n\n        a = (ngx_array_t *) ((char *) &r->headers_in + hh->offset);\n\n        n = a->nelts;\n\n        if (n == 0) {\n            XSRETURN_UNDEF;\n        }\n\n        ph = a->elts;\n\n        if (n == 1) {\n            ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len);\n\n            goto done;\n        }\n\n        size = - (ssize_t) (sizeof(\"; \") - 1);\n\n        for (i = 0; i < n; i++) {\n            size += ph[i]->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 (i = 0; /* void */ ; i++) {\n            p = ngx_copy(p, ph[i]->value.data, ph[i]->value.len);\n\n            if (i == n - 1) {\n                break;\n            }\n\n            *p++ = sep; *p++ = ' ';\n        }\n\n        ngx_http_perl_set_targ(value, size);\n\n        goto done;\n    }\n\n    /* iterate over all headers */\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        ngx_http_perl_set_targ(h[i].value.data, h[i].value.len);\n\n        goto done;\n    }\n\n    XSRETURN_UNDEF;\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\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\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\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    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_char) 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\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\n            || lsopt->type != port[i].type\n            || sa->sa_family != port[i].family)\n        {\n            continue;\n        }\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->type = lsopt->type;\n    port->port = p;\n    port->addrs.elts = NULL;\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    ngx_http_conf_addr_t  *addr;\n#if (NGX_HTTP_SSL)\n    ngx_uint_t             ssl;\n#endif\n#if (NGX_HTTP_QUIC)\n    ngx_uint_t             quic;\n#endif\n#if (NGX_HTTP_V2)\n    ngx_uint_t             http2;\n#endif\n#if (NGX_HTTP_V3)\n    ngx_uint_t             http3;\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\n#if (NGX_HTTP_SSL)\n        ssl = lsopt->ssl || addr[i].opt.ssl;\n#endif\n#if (NGX_HTTP_QUIC)\n        quic = lsopt->quic || addr[i].opt.quic;\n#endif\n#if (NGX_HTTP_V2)\n        http2 = lsopt->http2 || addr[i].opt.http2;\n#endif\n#if (NGX_HTTP_V3)\n        http3 = lsopt->http3 || addr[i].opt.http3;\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        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_QUIC)\n        addr[i].opt.quic = quic;\n#endif\n#if (NGX_HTTP_V2)\n        addr[i].opt.http2 = http2;\n#endif\n#if (NGX_HTTP_V3)\n        addr[i].opt.http3 = http3;\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#if (NGX_HTTP_QUIC && !defined NGX_QUIC)\n\n    if (lsopt->quic) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"nginx was built with OpenSSL that lacks QUIC \"\n                           \"support, QUIC 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->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\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\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->type = addr->opt.type;\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    ls->wildcard = addr->opt.wildcard;\n\n#if (NGX_HTTP_QUIC)\n    ls->quic = addr->opt.quic;\n#endif\n\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_QUIC)\n        addrs[i].conf.quic = addr[i].opt.quic;\n#endif\n#if (NGX_HTTP_V2)\n        addrs[i].conf.http2 = addr[i].opt.http2;\n#endif\n#if (NGX_HTTP_V3)\n        addrs[i].conf.http3 = addr[i].opt.http3;\n#endif\n        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\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        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_QUIC)\n        addrs6[i].conf.quic = addr[i].opt.quic;\n#endif\n#if (NGX_HTTP_V2)\n        addrs6[i].conf.http2 = addr[i].opt.http2;\n#endif\n#if (NGX_HTTP_V3)\n        addrs6[i].conf.http3 = addr[i].opt.http3;\n#endif\n        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\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"
  },
  {
    "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;\ntypedef struct ngx_http_v3_parse_s    ngx_http_v3_parse_t;\ntypedef struct ngx_http_v3_session_s  ngx_http_v3_session_t;\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 (NGX_HTTP_V2)\n#include <ngx_http_v2.h>\n#endif\n#if (NGX_HTTP_V3)\n#include <ngx_http_v3.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#if (NGX_HTTP_QUIC)\n#include <ngx_http_quic_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\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_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers,\n    ngx_str_t *name, ngx_str_t *value);\nngx_int_t ngx_http_parse_set_cookie_lines(ngx_array_t *headers,\n    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\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);\nngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,\n    ngx_str_t *host);\nngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,\n    ngx_uint_t alloc);\nvoid ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc);\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#if (NGX_HTTP_V2 || NGX_HTTP_V3)\nngx_int_t ngx_http_v2_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_v2_huff_encode(u_char *src, size_t len, u_char *dst,\n    ngx_uint_t lower);\n#endif\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\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#if (NGX_HAVE_AIO_SENDFILE)\nstatic ssize_t ngx_http_copy_aio_sendfile_preload(ngx_buf_t *file);\nstatic void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev);\n#endif\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#if (NGX_HAVE_AIO_SENDFILE)\n            ctx->aio_preload = ngx_http_copy_aio_sendfile_preload;\n#endif\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\n#if (NGX_HAVE_AIO_SENDFILE)\n\nstatic ssize_t\nngx_http_copy_aio_sendfile_preload(ngx_buf_t *file)\n{\n    ssize_t                  n;\n    static u_char            buf[1];\n    ngx_event_aio_t         *aio;\n    ngx_http_request_t      *r;\n    ngx_output_chain_ctx_t  *ctx;\n\n    n = ngx_file_aio_read(file->file, buf, 1, file->file_pos, NULL);\n\n    if (n == NGX_AGAIN) {\n        aio = file->file->aio;\n        aio->handler = ngx_http_copy_aio_sendfile_event_handler;\n\n        r = aio->data;\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\n    return n;\n}\n\n\nstatic void\nngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)\n{\n    ngx_event_aio_t     *aio;\n    ngx_http_request_t  *r;\n\n    aio = ev->data;\n    r = aio->data;\n\n    r->main->blocked--;\n    r->aio = 0;\n    ev->complete = 0;\n\n    r->connection->write->handler(r->connection->write);\n}\n\n#endif\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_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    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 (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);\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 (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    { 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    { 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    { 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    { 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    { 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 (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    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    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        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_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            if (r->headers_out.www_authenticate) {\n                r->headers_out.www_authenticate->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    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\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    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        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    if (r != r->main && val.len == 0) {\n        return ngx_http_send_header(r);\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\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_array_t               *cc;\n    ngx_table_elt_t           *e, *d, *ae;\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->elts) {\n\n        if ((p & NGX_HTTP_GZIP_PROXIED_NO_CACHE)\n            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_cache,\n                                                 NULL)\n               >= 0)\n        {\n            goto ok;\n        }\n\n        if ((p & NGX_HTTP_GZIP_PROXIED_NO_STORE)\n            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_store,\n                                                 NULL)\n               >= 0)\n        {\n            goto ok;\n        }\n\n        if ((p & NGX_HTTP_GZIP_PROXIED_PRIVATE)\n            && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_private,\n                                                 NULL)\n               >= 0)\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    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\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    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    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    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 (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_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,\n    int recursive)\n{\n    ngx_int_t          rc;\n    ngx_uint_t         i, found;\n    ngx_table_elt_t  **h;\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    i = headers->nelts;\n    h = headers->elts;\n\n    rc = NGX_DECLINED;\n\n    found = 0;\n\n    while (i-- > 0) {\n        rc = ngx_http_get_forwarded_addr_internal(r, addr, h[i]->value.data,\n                                                  h[i]->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    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\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\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    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    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    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 (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    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    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    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                              prev->lingering_close, NGX_HTTP_LINGERING_ON);\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, 1);\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    ngx_conf_merge_value(conf->chunked_transfer_encoding,\n                              prev->chunked_transfer_encoding, 1);\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;\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.type = SOCK_STREAM;\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, \"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_strcmp(value[n].data, \"quic\") == 0) {\n#if (NGX_HTTP_QUIC)\n            lsopt.quic = 1;\n            lsopt.type = SOCK_DGRAM;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"quic\\\" parameter requires \"\n                               \"ngx_http_quic_module\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[n].data, \"http3\") == 0) {\n#if (NGX_HTTP_V3)\n            lsopt.quic = 1;\n            lsopt.http3 = 1;\n            lsopt.type = SOCK_DGRAM;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"http3\\\" parameter requires \"\n                               \"ngx_http_v3_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        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[n]);\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_SSL)\n\n#if (NGX_HTTP_V3)\n    if (lsopt.ssl && lsopt.http3) {\n        return \"\\\"ssl\\\" parameter is incompatible with \\\"http3\\\"\";\n    }\n#endif\n\n#if (NGX_HTTP_QUIC)\n    if (lsopt.ssl && lsopt.quic) {\n        return \"\\\"ssl\\\" parameter is incompatible with \\\"quic\\\"\";\n    }\n#endif\n\n#endif\n\n    for (n = 0; n < u.naddrs; 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\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_HAVE_AIO_SENDFILE)\n\n    if (ngx_strcmp(value[1].data, \"sendfile\") == 0) {\n        clcf->aio = NGX_HTTP_AIO_ON;\n\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"the \\\"sendfile\\\" parameter of \"\n                           \"the \\\"aio\\\" directive is deprecated\");\n        return NGX_CONF_OK;\n    }\n\n#endif\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 (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"
  },
  {
    "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#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                   quic:1;\n    unsigned                   http2:1;\n    unsigned                   http3: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\n    int                        backlog;\n    int                        rcvbuf;\n    int                        sndbuf;\n    int                        type;\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\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\n    unsigned                   ssl:1;\n    unsigned                   quic:1;\n    unsigned                   http2:1;\n    unsigned                   http3:1;\n    unsigned                   proxy_protocol:1;\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    ngx_int_t                  type;\n    in_port_t                  port;\n    ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */\n} ngx_http_conf_port_t;\n\n\ntypedef struct {\n    ngx_http_listen_opt_t      opt;\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    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    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    ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */\n    ngx_flag_t    etag;                    /* etag */\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_char                           auto_redirect;\n    u_char                           len;\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\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_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,\n    int recursive);\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    if (r != r->main && c->length - c->body_start == 0) {\n        return ngx_http_send_header(r);\n    }\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\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        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\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\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    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 (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    }\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 (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    }\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_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        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                break;\n            }\n\n            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {\n                return NGX_HTTP_PARSE_INVALID_METHOD;\n            }\n\n            break;\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_int_t\nngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,\n    ngx_str_t *value)\n{\n    ngx_uint_t         i;\n    u_char            *start, *last, *end, ch;\n    ngx_table_elt_t  **h;\n\n    h = headers->elts;\n\n    for (i = 0; i < headers->nelts; i++) {\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,\n                       \"parse header: \\\"%V: %V\\\"\", &h[i]->key, &h[i]->value);\n\n        if (name->len > h[i]->value.len) {\n            continue;\n        }\n\n        start = h[i]->value.data;\n        end = h[i]->value.data + h[i]->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 i;\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 i;\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 NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_http_parse_set_cookie_lines(ngx_array_t *headers, ngx_str_t *name,\n    ngx_str_t *value)\n{\n    ngx_uint_t         i;\n    u_char            *start, *last, *end;\n    ngx_table_elt_t  **h;\n\n    h = headers->elts;\n\n    for (i = 0; i < headers->nelts; i++) {\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,\n                       \"parse header: \\\"%V: %V\\\"\", &h[i]->key, &h[i]->value);\n\n        if (name->len >= h[i]->value.len) {\n            continue;\n        }\n\n        start = h[i]->value.data;\n        end = h[i]->value.data + h[i]->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 i;\n    }\n\n    return NGX_DECLINED;\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"
  },
  {
    "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\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_multi_header_lines(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_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_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_multi_header_lines },\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, cookies),\n                 ngx_http_process_multi_header_lines },\n\n    { ngx_null_string, 0, NULL }\n};\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\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#if (NGX_HTTP_QUIC)\n    if (hc->addr_conf->quic) {\n        if (ngx_http_quic_init(c) == NGX_DONE) {\n            return;\n        }\n    }\n#endif\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)\n    if (hc->addr_conf->http2) {\n        rev->handler = ngx_http_v2_init;\n    }\n#endif\n\n#if (NGX_HTTP_V3)\n    if (hc->addr_conf->http3) {\n        ngx_http_v3_init(c);\n        return;\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_pfree(c->pool, b->start) == NGX_OK) {\n            b->start = NULL;\n        }\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\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    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    tp = ngx_timeofday();\n    r->start_sec = tp->sec;\n    r->start_msec = tp->msec;\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            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\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        hc = c->data;\n\n        if (hc->addr_conf->http2) {\n\n            SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\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    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 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_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    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 (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\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\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 (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    if (*ph == NULL) {\n        *ph = h;\n    }\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        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\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_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 (r->headers_in.user_agent) {\n        return NGX_OK;\n    }\n\n    r->headers_in.user_agent = h;\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\nstatic ngx_int_t\nngx_http_process_multi_header_lines(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_array_t       *headers;\n    ngx_table_elt_t  **ph;\n\n    headers = (ngx_array_t *) ((char *) &r->headers_in + offset);\n\n    if (headers->elts == NULL) {\n        if (ngx_array_init(headers, r->pool, 1, sizeof(ngx_table_elt_t *))\n            != NGX_OK)\n        {\n            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return NGX_ERROR;\n        }\n    }\n\n    ph = ngx_array_push(headers);\n    if (ph == NULL) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    *ph = h;\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 (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\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)\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\nngx_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\nngx_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 (NGX_HTTP_QUIC)\n    if (r->connection->quic) {\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    {\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 (NGX_HTTP_QUIC)\n\n    if (c->quic) {\n        if (c->read->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\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    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\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_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_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\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\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_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    } 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\nvoid\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    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#if (NGX_HTTP_QUIC)\n        && r->connection->quic == NULL\n#endif\n       )\n    {\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_HTTP_V3)\n    if (ngx_http_v3_connection(c)) {\n        ngx_http_v3_reset_connection(c);\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"
  },
  {
    "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#define NGX_HTTP_VERSION_30                3000\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_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_array_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_str_t                         user;\n    ngx_str_t                         passwd;\n\n    ngx_array_t                       cookies;\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                          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_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    ngx_array_t                       cache_control;\n    ngx_array_t                       link;\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#if (NGX_HTTP_V3 || NGX_COMPAT)\n    ngx_http_v3_session_t            *v3_session;\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    ngx_uint_t                        method;\n    ngx_uint_t                        http_version;\n\n    ngx_str_t                         request_line;\n    ngx_str_t                         uri;\n    ngx_str_t                         args;\n    ngx_str_t                         exten;\n    ngx_str_t                         unparsed_uri;\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    ngx_http_v3_parse_t              *v3_parse;\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\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                          response_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\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 (NGX_HTTP_V3)\n    if (r->http_version == NGX_HTTP_VERSION_30) {\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 (NGX_HTTP_V3)\n    if (r->http_version == NGX_HTTP_VERSION_30) {\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\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 (NGX_HTTP_V3)\n    if (r->http_version == NGX_HTTP_VERSION_30) {\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    {\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\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        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\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);\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\n\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\n\nstatic u_char ngx_http_error_tail[] =\n\"<hr><center>nginx</center>\" CRLF\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    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    u_char       *tail;\n    size_t        len;\n    ngx_int_t     rc;\n    ngx_buf_t    *b;\n    ngx_uint_t    msie_padding;\n    ngx_chain_t   out[3];\n\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\n    msie_padding = 0;\n\n    if (ngx_http_error_pages[err].len) {\n        r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;\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    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    out[0].buf = b;\n    out[0].next = &out[1];\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->memory = 1;\n\n    b->pos = tail;\n    b->last = tail + len;\n\n    out[1].buf = b;\n    out[1].next = NULL;\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        out[1].next = &out[2];\n        out[2].buf = b;\n        out[2].next = NULL;\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"
  },
  {
    "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\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);\nstatic void 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);\nstatic void 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 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\n#if (NGX_HTTP_GZIP)\nstatic ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\n#endif\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 (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\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_header_line,\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_ignore_header_line, 0,\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, cookies),\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_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, accept_ranges),\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#if (NGX_HTTP_GZIP)\n    { ngx_string(\"Content-Encoding\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, content_encoding),\n                 ngx_http_upstream_copy_content_encoding, 0, 0 },\n#endif\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    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_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 (NGX_HTTP_QUIC)\n    if (c->quic) {\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    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        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, 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\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 (NGX_HTTP_QUIC)\n\n    if (c->quic) {\n        if (c->write->error) {\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_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\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\nstatic void\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\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    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        ngx_add_timer(c->write, u->conf->connect_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    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 (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 && (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            ngx_add_timer(c->write, u->conf->connect_timeout);\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        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            ngx_add_timer(c->write, u->conf->send_timeout);\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        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        ngx_add_timer(c->read, u->conf->read_timeout);\n\n        if (c->read->ready) {\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            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;\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 = ngx_list_push(&r->headers_out.headers);\n\n                if (h == NULL) {\n                    ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    return NGX_OK;\n                }\n\n                *h = *u->headers_in.www_authenticate;\n\n                r->headers_out.www_authenticate = h;\n            }\n\n#if (NGX_HTTP_CACHE)\n\n            if (r->cache) {\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.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            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 (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    p->read_timeout = u->conf->read_timeout;\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        ngx_add_timer(upstream->write, u->conf->send_timeout);\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        ngx_add_timer(upstream->read, u->conf->read_timeout);\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    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        ngx_add_timer(upstream->read, u->conf->read_timeout);\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_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    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 (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    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http next upstream, %xi\", ft_type);\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 (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {\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    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\nstatic void\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 (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 (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 == NULL) {\n        *ph = h;\n    }\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    u->headers_in.content_length = h;\n    u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len);\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    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_array_t           *pa;\n    ngx_table_elt_t      **ph;\n    ngx_http_upstream_t   *u;\n\n    u = r->upstream;\n    pa = &u->headers_in.cookies;\n\n    if (pa->elts == NULL) {\n        if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    ph = ngx_array_push(pa);\n    if (ph == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ph = h;\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_array_t          *pa;\n    ngx_table_elt_t     **ph;\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n    pa = &u->headers_in.cache_control;\n\n    if (pa->elts == NULL) {\n        if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    ph = ngx_array_push(pa);\n    if (ph == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ph = h;\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    if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {\n        return NGX_OK;\n    }\n\n    start = h->value.data;\n    last = start + h->value.len;\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->cacheable = 0;\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->cacheable = 0;\n            return NGX_OK;\n        }\n\n        r->cache->valid_sec = ngx_time() + n;\n    }\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    u->headers_in.expires = h;\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->cacheable = 0;\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    u->headers_in.x_accel_expires = h;\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            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    }\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    u->headers_in.x_accel_limit_rate = h;\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    if (r->upstream->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    r->upstream->headers_in.connection = h;\n\n    if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,\n                         (u_char *) \"close\", 5 - 1)\n        != NULL)\n    {\n        r->upstream->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    r->upstream->headers_in.transfer_encoding = h;\n\n    if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,\n                         (u_char *) \"chunked\", 7 - 1)\n        != NULL)\n    {\n        r->upstream->headers_in.chunked = 1;\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_http_upstream_t  *u;\n\n    u = r->upstream;\n    u->headers_in.vary = h;\n\n#if (NGX_HTTP_CACHE)\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_VARY) {\n        return NGX_OK;\n    }\n\n    if (r->cache == NULL) {\n        return NGX_OK;\n    }\n\n    if (h->value.len > NGX_HTTP_CACHE_VARY_LEN\n        || (h->value.len == 1 && h->value.data[0] == '*'))\n    {\n        u->cacheable = 0;\n    }\n\n    r->cache->vary = h->value;\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    }\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_array_t      *pa;\n    ngx_table_elt_t  *ho, **ph;\n\n    pa = (ngx_array_t *) ((char *) &r->headers_out + offset);\n\n    if (pa->elts == NULL) {\n        if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\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_array_push(pa);\n    if (ph == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ph = ho;\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\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\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\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\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\n    r->headers_out.accept_ranges = ho;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HTTP_GZIP)\n\nstatic ngx_int_t\nngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,\n    ngx_table_elt_t *h, 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\n    r->headers_out.content_encoding = ho;\n\n    return NGX_OK;\n}\n\n#endif\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(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(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->upstream->headers_in.cookies,\n                                        &s, &cookie)\n        == NGX_DECLINED)\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    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\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        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\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    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    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_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\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    return uscf;\n}\n\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    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"
  },
  {
    "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} 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\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\n\nstruct ngx_http_upstream_srv_conf_s {\n    ngx_http_upstream_peer_t         peer;\n    void                           **srv_conf;\n\n    ngx_array_t                     *servers;  /* ngx_http_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_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#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                 *accept_ranges;\n    ngx_table_elt_t                 *www_authenticate;\n    ngx_table_elt_t                 *transfer_encoding;\n    ngx_table_elt_t                 *vary;\n\n#if (NGX_HTTP_GZIP)\n    ngx_table_elt_t                 *content_encoding;\n#endif\n\n    ngx_array_t                      cache_control;\n    ngx_array_t                      cookies;\n\n    off_t                            content_length_n;\n    time_t                           last_modified_time;\n\n    unsigned                         connection_close:1;\n    unsigned                         chunked: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\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\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\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\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\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\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_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\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_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\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_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    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        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(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            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(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        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\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    ngx_int_t                     total;\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    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_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\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\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    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\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\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(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);\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 (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\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_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/*\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    { 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_headers,\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.cookies), 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(\"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    { ngx_string(\"request_uri\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },\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_headers,\n      offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },\n\n    { ngx_string(\"sent_http_link\"), NULL, ngx_http_variable_headers,\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    { 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    { 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\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\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    ngx_table_elt_t  *h;\n\n    h = *(ngx_table_elt_t **) ((char *) r + data);\n\n    if (h) {\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    } else {\n        v->not_found = 1;\n    }\n\n    return NGX_OK;\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(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, *end;\n    ngx_uint_t         i, n;\n    ngx_array_t       *a;\n    ngx_table_elt_t  **h;\n\n    a = (ngx_array_t *) ((char *) r + data);\n\n    n = a->nelts;\n    h = a->elts;\n\n    len = 0;\n\n    for (i = 0; i < n; i++) {\n\n        if (h[i]->hash == 0) {\n            continue;\n        }\n\n        len += h[i]->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 (n == 1) {\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    end = p + len;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (h[i]->hash == 0) {\n            continue;\n        }\n\n        p = ngx_copy(p, h[i]->value.data, h[i]->value.len);\n\n        if (p == end) {\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(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(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(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_variable_value_t *v, ngx_str_t *var,\n    ngx_list_part_t *part, size_t prefix)\n{\n    u_char            ch;\n    ngx_uint_t        i, n;\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        for (n = 0; n + prefix < var->len && 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            if (var->data[n + prefix] != ch) {\n                break;\n            }\n        }\n\n        if (n + prefix == var->len && n == header[i].key.len) {\n            v->len = header[i].value.len;\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n            v->data = header[i].value.data;\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_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->headers_in.cookies, &s, &cookie)\n        == NGX_DECLINED)\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\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_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    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(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\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_variable_value_t *v,\n    ngx_str_t *var, ngx_list_part_t *part, 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\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    {\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            if (last) {\n                r->response_sent = 1;\n            }\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    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 (last) {\n        r->response_sent = 1;\n    }\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\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->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 (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_v2_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_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\n    /* TODO Optimization: validate headers while parsing. */\n    if (ngx_http_v2_validate_header(r, header) != NGX_OK) {\n        if (ngx_http_v2_terminate_stream(h2c, h2c->state.stream,\n                                         NGX_HTTP_V2_PROTOCOL_ERROR)\n            == NGX_ERROR)\n        {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\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    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                       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                       last_sid;\n    ngx_uint_t                       last_push;\n\n    time_t                           lingering_time;\n\n    unsigned                         closed_nodes:8;\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_v2_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    static const u_char nginx[5] = \"\\x84\\xaa\\x63\\x55\\xe7\";\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    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\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                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n                                            sizeof(NGINX_VER) - 1, tmp);\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                p = ngx_http_v2_write_value(nginx_ver_build,\n                                            (u_char *) NGINX_VER_BUILD,\n                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\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\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    h = r->headers_out.link.elts;\n\n    for (i = 0; i < r->headers_out.link.nelts; i++) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http2 parse link: \\\"%V\\\"\", &h[i]->value);\n\n        start = h[i]->value.data;\n        end = h[i]->value.data + h[i]->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    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 (stream->queued) {\n            fc->write->active = 1;\n            fc->write->ready = 0;\n\n        } else {\n            fc->buffered &= ~NGX_HTTP_V2_BUFFERED;\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        fc->write->active = 1;\n        fc->write->ready = 0;\n        return in;\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    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_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_v2_huff_decode_code_t;\n\n\nstatic ngx_inline ngx_int_t ngx_http_v2_huff_decode_bits(u_char *state,\n    u_char *ending, ngx_uint_t bits, u_char **dst);\n\n\nstatic ngx_http_v2_huff_decode_code_t  ngx_http_v2_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_v2_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_v2_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_v2_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_v2_huff_decode_bits(u_char *state, u_char *ending, ngx_uint_t bits,\n    u_char **dst)\n{\n    ngx_http_v2_huff_decode_code_t  code;\n\n    code = ngx_http_v2_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/v2/ngx_http_v2_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_v2_huff_encode_code_t;\n\n\nstatic ngx_http_v2_huff_encode_code_t  ngx_http_v2_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_v2_huff_encode_code_t  ngx_http_v2_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_v2_huff_encode_buf(dst, buf)                                 \\\n    (*(uint64_t *) (dst) = __builtin_bswap64(buf))\n#else\n#define ngx_http_v2_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_v2_huff_encode_buf(dst, buf)                                 \\\n    (*(uint64_t *) (dst) = (buf))\n#endif\n\n#else /* NGX_PTR_SIZE == 4 */\n\n#define ngx_http_v2_huff_encode_buf(dst, buf)                                 \\\n    (*(uint32_t *) (dst) = htonl(buf))\n\n#endif\n\n\nsize_t\nngx_http_v2_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_v2_huff_encode_code_t  *table, *next;\n\n    table = lower ? ngx_http_v2_huff_encode_table_lc\n                  : ngx_http_v2_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_v2_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/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      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    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    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} 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/http/v3/ngx_http_v3.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\nstatic void ngx_http_v3_keepalive_handler(ngx_event_t *ev);\nstatic void ngx_http_v3_cleanup_session(void *data);\n\n\nngx_int_t\nngx_http_v3_init_session(ngx_connection_t *c)\n{\n    ngx_connection_t       *pc;\n    ngx_pool_cleanup_t     *cln;\n    ngx_http_connection_t  *hc;\n    ngx_http_v3_session_t  *h3c;\n\n    pc = c->quic->parent;\n    hc = pc->data;\n\n    if (hc->v3_session) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 init session\");\n\n    h3c = ngx_pcalloc(pc->pool, sizeof(ngx_http_v3_session_t));\n    if (h3c == NULL) {\n        goto failed;\n    }\n\n    h3c->max_push_id = (uint64_t) -1;\n    h3c->goaway_push_id = (uint64_t) -1;\n\n    ngx_queue_init(&h3c->blocked);\n    ngx_queue_init(&h3c->pushing);\n\n    h3c->keepalive.log = pc->log;\n    h3c->keepalive.data = pc;\n    h3c->keepalive.handler = ngx_http_v3_keepalive_handler;\n    h3c->keepalive.cancelable = 1;\n\n    cln = ngx_pool_cleanup_add(pc->pool, 0);\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    cln->handler = ngx_http_v3_cleanup_session;\n    cln->data = h3c;\n\n    hc->v3_session = h3c;\n\n    return ngx_http_v3_send_settings(c);\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create http3 session\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR,\n                                    \"failed to create http3 session\");\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_v3_keepalive_handler(ngx_event_t *ev)\n{\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 keepalive handler\");\n\n    ngx_quic_finalize_connection(c, NGX_OK, NGX_HTTP_V3_ERR_NO_ERROR,\n                                 \"keepalive timeout\");\n}\n\n\nstatic void\nngx_http_v3_cleanup_session(void *data)\n{\n    ngx_http_v3_session_t  *h3c = data;\n\n    ngx_http_v3_cleanup_table(h3c);\n\n    if (h3c->keepalive.timer_set) {\n        ngx_del_timer(&h3c->keepalive);\n    }\n}\n\n\nngx_int_t\nngx_http_v3_check_flood(ngx_connection_t *c)\n{\n    ngx_http_v3_session_t  *h3c;\n\n    h3c = ngx_http_v3_get_session(c);\n\n    if (h3c->total_bytes / 8 > h3c->payload_bytes + 1048576) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"http3 flood detected\");\n\n        ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_NO_ERROR,\n                                        \"HTTP/3 flood detected\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_V3_H_INCLUDED_\n#define _NGX_HTTP_V3_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <ngx_http_v3_parse.h>\n#include <ngx_http_v3_encode.h>\n#include <ngx_http_v3_streams.h>\n#include <ngx_http_v3_tables.h>\n\n\n#define NGX_HTTP_V3_ALPN_PROTO                     \"\\x02h3\"\n#define NGX_HTTP_V3_ALPN_DRAFT_FMT                 \"\\x05h3-%02uD\"\n\n#define NGX_HTTP_V3_VARLEN_INT_LEN                 4\n#define NGX_HTTP_V3_PREFIX_INT_LEN                 11\n\n#define NGX_HTTP_V3_STREAM_CONTROL                 0x00\n#define NGX_HTTP_V3_STREAM_PUSH                    0x01\n#define NGX_HTTP_V3_STREAM_ENCODER                 0x02\n#define NGX_HTTP_V3_STREAM_DECODER                 0x03\n\n#define NGX_HTTP_V3_FRAME_DATA                     0x00\n#define NGX_HTTP_V3_FRAME_HEADERS                  0x01\n#define NGX_HTTP_V3_FRAME_CANCEL_PUSH              0x03\n#define NGX_HTTP_V3_FRAME_SETTINGS                 0x04\n#define NGX_HTTP_V3_FRAME_PUSH_PROMISE             0x05\n#define NGX_HTTP_V3_FRAME_GOAWAY                   0x07\n#define NGX_HTTP_V3_FRAME_MAX_PUSH_ID              0x0d\n\n#define NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY       0x01\n#define NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE     0x06\n#define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS          0x07\n\n#define NGX_HTTP_V3_STREAM_CLIENT_CONTROL          0\n#define NGX_HTTP_V3_STREAM_SERVER_CONTROL          1\n#define NGX_HTTP_V3_STREAM_CLIENT_ENCODER          2\n#define NGX_HTTP_V3_STREAM_SERVER_ENCODER          3\n#define NGX_HTTP_V3_STREAM_CLIENT_DECODER          4\n#define NGX_HTTP_V3_STREAM_SERVER_DECODER          5\n#define NGX_HTTP_V3_MAX_KNOWN_STREAM               6\n\n/* HTTP/3 errors */\n#define NGX_HTTP_V3_ERR_NO_ERROR                   0x100\n#define NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR     0x101\n#define NGX_HTTP_V3_ERR_INTERNAL_ERROR             0x102\n#define NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR      0x103\n#define NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM     0x104\n#define NGX_HTTP_V3_ERR_FRAME_UNEXPECTED           0x105\n#define NGX_HTTP_V3_ERR_FRAME_ERROR                0x106\n#define NGX_HTTP_V3_ERR_EXCESSIVE_LOAD             0x107\n#define NGX_HTTP_V3_ERR_ID_ERROR                   0x108\n#define NGX_HTTP_V3_ERR_SETTINGS_ERROR             0x109\n#define NGX_HTTP_V3_ERR_MISSING_SETTINGS           0x10a\n#define NGX_HTTP_V3_ERR_REQUEST_REJECTED           0x10b\n#define NGX_HTTP_V3_ERR_REQUEST_CANCELLED          0x10c\n#define NGX_HTTP_V3_ERR_REQUEST_INCOMPLETE         0x10d\n#define NGX_HTTP_V3_ERR_CONNECT_ERROR              0x10f\n#define NGX_HTTP_V3_ERR_VERSION_FALLBACK           0x110\n\n/* QPACK errors */\n#define NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED       0x200\n#define NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR       0x201\n#define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR       0x202\n\n\n#define ngx_http_v3_get_session(c)  ngx_http_quic_get_connection(c)->v3_session\n\n#define ngx_http_v3_get_module_loc_conf(c, module)                            \\\n    ngx_http_get_module_loc_conf(ngx_http_quic_get_connection(c)->conf_ctx,     \\\n                                 module)\n\n#define ngx_http_v3_get_module_srv_conf(c, module)                            \\\n    ngx_http_get_module_srv_conf(ngx_http_quic_get_connection(c)->conf_ctx,     \\\n                                 module)\n\n#define ngx_http_v3_finalize_connection(c, code, reason)                      \\\n    ngx_quic_finalize_connection(c->quic->parent, ((code == NGX_HTTP_V3_ERR_NO_ERROR) ? NGX_OK : NGX_ERROR), code, reason)\n\n#define ngx_http_v3_shutdown_connection(c, code, reason)                      \\\n    ngx_quic_shutdown_connection(c->quic->parent, ((code == NGX_HTTP_V3_ERR_NO_ERROR) ? NGX_OK : NGX_ERROR), code, reason)\n\n#define ngx_http_v3_connection(c)                                             \\\n    ((c)->quic ? ngx_http_quic_get_connection(c)->addr_conf->http3 : 0)\n\n\ntypedef struct {\n    size_t                        max_table_capacity;\n    ngx_uint_t                    max_blocked_streams;\n    ngx_uint_t                    max_concurrent_pushes;\n    ngx_uint_t                    max_uni_streams;\n} ngx_http_v3_srv_conf_t;\n\n\ntypedef struct {\n    ngx_flag_t                    push_preload;\n    ngx_flag_t                    push;\n    ngx_array_t                  *pushes;\n} ngx_http_v3_loc_conf_t;\n\n\nstruct ngx_http_v3_parse_s {\n    size_t                        header_limit;\n    ngx_http_v3_parse_headers_t   headers;\n    ngx_http_v3_parse_data_t      body;\n};\n\n\nstruct ngx_http_v3_session_s {\n    ngx_http_v3_dynamic_table_t   table;\n\n    ngx_event_t                   keepalive;\n    ngx_uint_t                    nrequests;\n\n    ngx_queue_t                   blocked;\n    ngx_uint_t                    nblocked;\n\n    ngx_queue_t                   pushing;\n    ngx_uint_t                    npushing;\n    uint64_t                      next_push_id;\n    uint64_t                      max_push_id;\n    uint64_t                      goaway_push_id;\n\n    off_t                         total_bytes;\n    off_t                         payload_bytes;\n\n    ngx_uint_t                    goaway;  /* unsigned  goaway:1; */\n\n    ngx_connection_t             *known_streams[NGX_HTTP_V3_MAX_KNOWN_STREAM];\n};\n\n\nvoid ngx_http_v3_init(ngx_connection_t *c);\nvoid ngx_http_v3_reset_connection(ngx_connection_t *c);\nngx_int_t ngx_http_v3_init_session(ngx_connection_t *c);\nngx_int_t ngx_http_v3_check_flood(ngx_connection_t *c);\n\nngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r);\nngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r);\n\n\nextern ngx_module_t  ngx_http_v3_module;\n\n\n#endif /* _NGX_HTTP_V3_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_encode.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\nuintptr_t\nngx_http_v3_encode_varlen_int(u_char *p, uint64_t value)\n{\n    if (value <= 63) {\n        if (p == NULL) {\n            return 1;\n        }\n\n        *p++ = value;\n        return (uintptr_t) p;\n    }\n\n    if (value <= 16383) {\n        if (p == NULL) {\n            return 2;\n        }\n\n        *p++ = 0x40 | (value >> 8);\n        *p++ = value;\n        return (uintptr_t) p;\n    }\n\n    if (value <= 1073741823) {\n        if (p == NULL) {\n            return 4;\n        }\n\n        *p++ = 0x80 | (value >> 24);\n        *p++ = (value >> 16);\n        *p++ = (value >> 8);\n        *p++ = value;\n        return (uintptr_t) p;\n    }\n\n    if (p == NULL) {\n        return 8;\n    }\n\n    *p++ = 0xc0 | (value >> 56);\n    *p++ = (value >> 48);\n    *p++ = (value >> 40);\n    *p++ = (value >> 32);\n    *p++ = (value >> 24);\n    *p++ = (value >> 16);\n    *p++ = (value >> 8);\n    *p++ = value;\n    return (uintptr_t) p;\n}\n\n\nuintptr_t\nngx_http_v3_encode_prefix_int(u_char *p, uint64_t value, ngx_uint_t prefix)\n{\n    ngx_uint_t  thresh, n;\n\n    thresh = (1 << prefix) - 1;\n\n    if (value < thresh) {\n        if (p == NULL) {\n            return 1;\n        }\n\n        *p++ |= value;\n        return (uintptr_t) p;\n    }\n\n    value -= thresh;\n\n    if (p == NULL) {\n        for (n = 2; value >= 128; n++) {\n            value >>= 7;\n        }\n\n        return n;\n    }\n\n    *p++ |= thresh;\n\n    while (value >= 128) {\n        *p++ = 0x80 | value;\n        value >>= 7;\n    }\n\n    *p++ = value;\n\n    return (uintptr_t) p;\n}\n\n\nuintptr_t\nngx_http_v3_encode_field_section_prefix(u_char *p, ngx_uint_t insert_count,\n    ngx_uint_t sign, ngx_uint_t delta_base)\n{\n    if (p == NULL) {\n        return ngx_http_v3_encode_prefix_int(NULL, insert_count, 8)\n               + ngx_http_v3_encode_prefix_int(NULL, delta_base, 7);\n    }\n\n    *p = 0;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, insert_count, 8);\n\n    *p = sign ? 0x80 : 0;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, delta_base, 7);\n\n    return (uintptr_t) p;\n}\n\n\nuintptr_t\nngx_http_v3_encode_field_ri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index)\n{\n    /* Indexed Field Line */\n\n    if (p == NULL) {\n        return ngx_http_v3_encode_prefix_int(NULL, index, 6);\n    }\n\n    *p = dynamic ? 0x80 : 0xc0;\n\n    return ngx_http_v3_encode_prefix_int(p, index, 6);\n}\n\n\nuintptr_t\nngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic, ngx_uint_t index,\n    u_char *data, size_t len)\n{\n    size_t   hlen;\n    u_char  *p1, *p2;\n\n    /* Literal Field Line With Name Reference */\n\n    if (p == NULL) {\n        return ngx_http_v3_encode_prefix_int(NULL, index, 4)\n               + ngx_http_v3_encode_prefix_int(NULL, len, 7)\n               + len;\n    }\n\n    *p = dynamic ? 0x40 : 0x50;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 4);\n\n    p1 = p;\n    *p = 0;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7);\n\n    if (data) {\n        p2 = p;\n        hlen = ngx_http_v2_huff_encode(data, len, p, 0);\n\n        if (hlen) {\n            p = p1;\n            *p = 0x80;\n            p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7);\n\n            if (p != p2) {\n                ngx_memmove(p, p2, hlen);\n            }\n\n            p += hlen;\n\n        } else {\n            p = ngx_cpymem(p, data, len);\n        }\n    }\n\n    return (uintptr_t) p;\n}\n\n\nuintptr_t\nngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name, ngx_str_t *value)\n{\n    size_t   hlen;\n    u_char  *p1, *p2;\n\n    /* Literal Field Line With Literal Name */\n\n    if (p == NULL) {\n        return ngx_http_v3_encode_prefix_int(NULL, name->len, 3)\n               + name->len\n               + ngx_http_v3_encode_prefix_int(NULL, value->len, 7)\n               + value->len;\n    }\n\n    p1 = p;\n    *p = 0x20;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, name->len, 3);\n\n    p2 = p;\n    hlen = ngx_http_v2_huff_encode(name->data, name->len, p, 1);\n\n    if (hlen) {\n        p = p1;\n        *p = 0x28;\n        p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 3);\n\n        if (p != p2) {\n            ngx_memmove(p, p2, hlen);\n        }\n\n        p += hlen;\n\n    } else {\n        ngx_strlow(p, name->data, name->len);\n        p += name->len;\n    }\n\n    p1 = p;\n    *p = 0;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, value->len, 7);\n\n    p2 = p;\n    hlen = ngx_http_v2_huff_encode(value->data, value->len, p, 0);\n\n    if (hlen) {\n        p = p1;\n        *p = 0x80;\n        p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7);\n\n        if (p != p2) {\n            ngx_memmove(p, p2, hlen);\n        }\n\n        p += hlen;\n\n    } else {\n        p = ngx_cpymem(p, value->data, value->len);\n    }\n\n    return (uintptr_t) p;\n}\n\n\nuintptr_t\nngx_http_v3_encode_field_pbi(u_char *p, ngx_uint_t index)\n{\n    /* Indexed Field Line With Post-Base Index */\n\n    if (p == NULL) {\n        return ngx_http_v3_encode_prefix_int(NULL, index, 4);\n    }\n\n    *p = 0x10;\n\n    return ngx_http_v3_encode_prefix_int(p, index, 4);\n}\n\n\nuintptr_t\nngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index, u_char *data,\n    size_t len)\n{\n    size_t   hlen;\n    u_char  *p1, *p2;\n\n    /* Literal Field Line With Post-Base Name Reference */\n\n    if (p == NULL) {\n        return ngx_http_v3_encode_prefix_int(NULL, index, 3)\n               + ngx_http_v3_encode_prefix_int(NULL, len, 7)\n               + len;\n    }\n\n    *p = 0;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, index, 3);\n\n    p1 = p;\n    *p = 0;\n    p = (u_char *) ngx_http_v3_encode_prefix_int(p, len, 7);\n\n    if (data) {\n        p2 = p;\n        hlen = ngx_http_v2_huff_encode(data, len, p, 0);\n\n        if (hlen) {\n            p = p1;\n            *p = 0x80;\n            p = (u_char *) ngx_http_v3_encode_prefix_int(p, hlen, 7);\n\n            if (p != p2) {\n                ngx_memmove(p, p2, hlen);\n            }\n\n            p += hlen;\n\n        } else {\n            p = ngx_cpymem(p, data, len);\n        }\n    }\n\n    return (uintptr_t) p;\n}\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_encode.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_V3_ENCODE_H_INCLUDED_\n#define _NGX_HTTP_V3_ENCODE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nuintptr_t ngx_http_v3_encode_varlen_int(u_char *p, uint64_t value);\nuintptr_t ngx_http_v3_encode_prefix_int(u_char *p, uint64_t value,\n    ngx_uint_t prefix);\n\nuintptr_t ngx_http_v3_encode_field_section_prefix(u_char *p,\n    ngx_uint_t insert_count, ngx_uint_t sign, ngx_uint_t delta_base);\nuintptr_t ngx_http_v3_encode_field_ri(u_char *p, ngx_uint_t dynamic,\n    ngx_uint_t index);\nuintptr_t ngx_http_v3_encode_field_lri(u_char *p, ngx_uint_t dynamic,\n    ngx_uint_t index, u_char *data, size_t len);\nuintptr_t ngx_http_v3_encode_field_l(u_char *p, ngx_str_t *name,\n    ngx_str_t *value);\nuintptr_t ngx_http_v3_encode_field_pbi(u_char *p, ngx_uint_t index);\nuintptr_t ngx_http_v3_encode_field_lpbi(u_char *p, ngx_uint_t index,\n    u_char *data, size_t len);\n\n\n#endif /* _NGX_HTTP_V3_ENCODE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_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\n/* static table indices */\n#define NGX_HTTP_V3_HEADER_AUTHORITY                 0\n#define NGX_HTTP_V3_HEADER_PATH_ROOT                 1\n#define NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO       4\n#define NGX_HTTP_V3_HEADER_DATE                      6\n#define NGX_HTTP_V3_HEADER_LAST_MODIFIED             10\n#define NGX_HTTP_V3_HEADER_LOCATION                  12\n#define NGX_HTTP_V3_HEADER_METHOD_GET                17\n#define NGX_HTTP_V3_HEADER_SCHEME_HTTP               22\n#define NGX_HTTP_V3_HEADER_SCHEME_HTTPS              23\n#define NGX_HTTP_V3_HEADER_STATUS_200                25\n#define NGX_HTTP_V3_HEADER_ACCEPT_ENCODING           31\n#define NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN   53\n#define NGX_HTTP_V3_HEADER_VARY_ACCEPT_ENCODING      59\n#define NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE           72\n#define NGX_HTTP_V3_HEADER_SERVER                    92\n#define NGX_HTTP_V3_HEADER_USER_AGENT                95\n\n\ntypedef struct {\n    ngx_chain_t         *free;\n    ngx_chain_t         *busy;\n} ngx_http_v3_filter_ctx_t;\n\n\nstatic ngx_int_t ngx_http_v3_header_filter(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_v3_push_resources(ngx_http_request_t *r,\n    ngx_chain_t ***out);\nstatic ngx_int_t ngx_http_v3_push_resource(ngx_http_request_t *r,\n    ngx_str_t *path, ngx_chain_t ***out);\nstatic ngx_int_t ngx_http_v3_create_push_request(\n    ngx_http_request_t *pr, ngx_str_t *path, uint64_t push_id);\nstatic ngx_int_t ngx_http_v3_set_push_header(ngx_http_request_t *r,\n    const char *name, ngx_str_t *value);\nstatic void ngx_http_v3_push_request_handler(ngx_event_t *ev);\nstatic ngx_chain_t *ngx_http_v3_create_push_promise(ngx_http_request_t *r,\n    ngx_str_t *path, uint64_t push_id);\nstatic ngx_int_t ngx_http_v3_body_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\nstatic ngx_chain_t *ngx_http_v3_create_trailers(ngx_http_request_t *r,\n    ngx_http_v3_filter_ctx_t *ctx);\nstatic ngx_int_t ngx_http_v3_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_http_module_t  ngx_http_v3_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_v3_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_v3_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_v3_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_v3_header_filter(ngx_http_request_t *r)\n{\n    u_char                    *p;\n    size_t                     len, n;\n    ngx_buf_t                 *b;\n    ngx_str_t                  host, location;\n    ngx_uint_t                 i, port;\n    ngx_chain_t               *out, *hl, *cl, **ll;\n    ngx_list_part_t           *part;\n    ngx_table_elt_t           *header;\n    ngx_connection_t          *c;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_v3_filter_ctx_t  *ctx;\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->http_version != NGX_HTTP_VERSION_30) {\n        return ngx_http_next_header_filter(r);\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        return NGX_OK;\n    }\n\n    h3c = ngx_http_v3_get_session(r->connection);\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->headers_out.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    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {\n        r->header_only = 1;\n    }\n\n    c = r->connection;\n\n    out = NULL;\n    ll = &out;\n\n    if ((c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) == 0\n        && r->method != NGX_HTTP_HEAD)\n    {\n        if (ngx_http_v3_push_resources(r, &ll) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    len = ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0);\n\n    if (r->headers_out.status == NGX_HTTP_OK) {\n        len += ngx_http_v3_encode_field_ri(NULL, 0,\n                                           NGX_HTTP_V3_HEADER_STATUS_200);\n\n    } else {\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                            NGX_HTTP_V3_HEADER_STATUS_200,\n                                            NULL, 3);\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 (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n            n = sizeof(NGINX_VER) - 1;\n\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n            n = sizeof(NGINX_VER_BUILD) - 1;\n\n        } else {\n            n = sizeof(\"nginx\") - 1;\n        }\n\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                            NGX_HTTP_V3_HEADER_SERVER,\n                                            NULL, n);\n    }\n\n    if (r->headers_out.date == NULL) {\n        len += ngx_http_v3_encode_field_lri(NULL, 0, NGX_HTTP_V3_HEADER_DATE,\n                                            NULL, ngx_cached_http_time.len);\n    }\n\n    if (r->headers_out.content_type.len) {\n        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            n += sizeof(\"; charset=\") - 1 + r->headers_out.charset.len;\n        }\n\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                    NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN,\n                                    NULL, n);\n    }\n\n    if (r->headers_out.content_length == NULL) {\n        if (r->headers_out.content_length_n > 0) {\n            len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                        NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO,\n                                        NULL, NGX_OFF_T_LEN);\n\n        } else if (r->headers_out.content_length_n == 0) {\n            len += ngx_http_v3_encode_field_ri(NULL, 0,\n                                       NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO);\n        }\n    }\n\n    if (r->headers_out.last_modified == NULL\n        && r->headers_out.last_modified_time != -1)\n    {\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                  NGX_HTTP_V3_HEADER_LAST_MODIFIED, NULL,\n                                  sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\") - 1);\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(c, &host, 0) != NGX_OK) {\n                    return NGX_ERROR;\n                }\n            }\n\n            port = ngx_inet_get_port(c->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                port = (port == 443) ? 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, \"https://\", sizeof(\"https://\") - 1);\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 += ngx_http_v3_encode_field_lri(NULL, 0,\n                                           NGX_HTTP_V3_HEADER_LOCATION, NULL,\n                                           r->headers_out.location->value.len);\n    }\n\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        if (clcf->gzip_vary) {\n            len += ngx_http_v3_encode_field_ri(NULL, 0,\n                                      NGX_HTTP_V3_HEADER_VARY_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        len += ngx_http_v3_encode_field_l(NULL, &header[i].key,\n                                          &header[i].value);\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 header len:%uz\", len);\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last,\n                                                                 0, 0, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 output header: \\\":status: %03ui\\\"\",\n                   r->headers_out.status);\n\n    if (r->headers_out.status == NGX_HTTP_OK) {\n        b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,\n                                                NGX_HTTP_V3_HEADER_STATUS_200);\n\n    } else {\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                                 NGX_HTTP_V3_HEADER_STATUS_200,\n                                                 NULL, 3);\n        b->last = ngx_sprintf(b->last, \"%03ui\", r->headers_out.status);\n    }\n\n    if (r->headers_out.server == NULL) {\n        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n            p = (u_char *) NGINX_VER;\n            n = sizeof(NGINX_VER) - 1;\n\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n            p = (u_char *) NGINX_VER_BUILD;\n            n = sizeof(NGINX_VER_BUILD) - 1;\n\n        } else {\n            p = (u_char *) \"nginx\";\n            n = sizeof(\"nginx\") - 1;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 output header: \\\"server: %*s\\\"\", n, p);\n\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                                     NGX_HTTP_V3_HEADER_SERVER,\n                                                     p, n);\n    }\n\n    if (r->headers_out.date == NULL) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 output header: \\\"date: %V\\\"\",\n                       &ngx_cached_http_time);\n\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                                     NGX_HTTP_V3_HEADER_DATE,\n                                                     ngx_cached_http_time.data,\n                                                     ngx_cached_http_time.len);\n    }\n\n    if (r->headers_out.content_type.len) {\n        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n            && r->headers_out.charset.len)\n        {\n            n = r->headers_out.content_type.len + sizeof(\"; charset=\") - 1\n                + r->headers_out.charset.len;\n\n            p = ngx_pnalloc(r->pool, n);\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 = n;\n            r->headers_out.content_type.data = p - n;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 output header: \\\"content-type: %V\\\"\",\n                       &r->headers_out.content_type);\n\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                    NGX_HTTP_V3_HEADER_CONTENT_TYPE_TEXT_PLAIN,\n                                    r->headers_out.content_type.data,\n                                    r->headers_out.content_type.len);\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, c->log, 0,\n                       \"http3 output header: \\\"content-length: %O\\\"\",\n                       r->headers_out.content_length_n);\n\n        if (r->headers_out.content_length_n > 0) {\n            p = ngx_sprintf(b->last, \"%O\", r->headers_out.content_length_n);\n            n = p - b->last;\n\n            b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                        NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO,\n                                        NULL, n);\n\n            b->last = ngx_sprintf(b->last, \"%O\",\n                                  r->headers_out.content_length_n);\n\n        } else {\n            b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,\n                                       NGX_HTTP_V3_HEADER_CONTENT_LENGTH_ZERO);\n        }\n    }\n\n    if (r->headers_out.last_modified == NULL\n        && r->headers_out.last_modified_time != -1)\n    {\n        n = sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\") - 1;\n\n        p = ngx_pnalloc(r->pool, n);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_time(p, r->headers_out.last_modified_time);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 output header: \\\"last-modified: %*s\\\"\", n, p);\n\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                              NGX_HTTP_V3_HEADER_LAST_MODIFIED,\n                                              p, n);\n    }\n\n    if (r->headers_out.location && r->headers_out.location->value.len) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 output header: \\\"location: %V\\\"\",\n                       &r->headers_out.location->value);\n\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                           NGX_HTTP_V3_HEADER_LOCATION,\n                                           r->headers_out.location->value.data,\n                                           r->headers_out.location->value.len);\n    }\n\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 output header: \\\"vary: Accept-Encoding\\\"\");\n\n        b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,\n                                      NGX_HTTP_V3_HEADER_VARY_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        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 output header: \\\"%V: %V\\\"\",\n                       &header[i].key, &header[i].value);\n\n        b->last = (u_char *) ngx_http_v3_encode_field_l(b->last,\n                                                        &header[i].key,\n                                                        &header[i].value);\n    }\n\n    if (r->header_only) {\n        b->last_buf = 1;\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    n = b->last - b->pos;\n\n    h3c->payload_bytes += n;\n\n    len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_HEADERS)\n          + ngx_http_v3_encode_varlen_int(NULL, n);\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,\n                                                    NGX_HTTP_V3_FRAME_HEADERS);\n    b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n);\n\n    hl = ngx_alloc_chain_link(r->pool);\n    if (hl == NULL) {\n        return NGX_ERROR;\n    }\n\n    hl->buf = b;\n    hl->next = cl;\n\n    *ll = hl;\n    ll = &cl->next;\n\n    if (r->headers_out.content_length_n >= 0\n        && !r->header_only && !r->expect_trailers)\n    {\n        len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_DATA)\n              + ngx_http_v3_encode_varlen_int(NULL,\n                                              r->headers_out.content_length_n);\n\n        b = ngx_create_temp_buf(r->pool, len);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,\n                                                       NGX_HTTP_V3_FRAME_DATA);\n        b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,\n                                              r->headers_out.content_length_n);\n\n        h3c->payload_bytes += r->headers_out.content_length_n;\n        h3c->total_bytes += r->headers_out.content_length_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        *ll = cl;\n\n    } else {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_filter_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_v3_filter_module);\n    }\n\n    for (cl = out; cl; cl = cl->next) {\n        h3c->total_bytes += cl->buf->last - cl->buf->pos;\n    }\n\n    return ngx_http_write_filter(r, out);\n}\n\n\nstatic ngx_int_t\nngx_http_v3_push_resources(ngx_http_request_t *r, ngx_chain_t ***out)\n{\n    u_char                     *start, *end, *last;\n    ngx_str_t                   path;\n    ngx_int_t                   rc;\n    ngx_uint_t                  i, push;\n    ngx_table_elt_t           **h;\n    ngx_http_v3_loc_conf_t     *h3lcf;\n    ngx_http_complex_value_t   *pushes;\n\n    h3lcf = ngx_http_get_module_loc_conf(r, ngx_http_v3_module);\n\n    if (h3lcf->pushes) {\n        pushes = h3lcf->pushes->elts;\n\n        for (i = 0; i < h3lcf->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_v3_push_resource(r, &path, out);\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 (!h3lcf->push_preload) {\n        return NGX_OK;\n    }\n\n    h = r->headers_out.link.elts;\n\n    for (i = 0; i < r->headers_out.link.nelts; i++) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 parse link: \\\"%V\\\"\", &h[i]->value);\n\n        start = h[i]->value.data;\n        end = h[i]->value.data + h[i]->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_v3_push_resource(r, &path, out);\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_v3_push_resource(ngx_http_request_t *r, ngx_str_t *path,\n    ngx_chain_t ***ll)\n{\n    uint64_t                 push_id;\n    ngx_int_t                rc;\n    ngx_chain_t             *cl;\n    ngx_connection_t        *c;\n    ngx_http_v3_session_t   *h3c;\n    ngx_http_v3_srv_conf_t  *h3scf;\n\n    c = r->connection;\n    h3c = ngx_http_v3_get_session(c);\n    h3scf = ngx_http_get_module_srv_conf(r, ngx_http_v3_module);\n\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 push \\\"%V\\\" pushing:%ui/%ui id:%uL/%L\",\n                   path, h3c->npushing, h3scf->max_concurrent_pushes,\n                   h3c->next_push_id, h3c->max_push_id);\n\n    if (!ngx_path_separator(path->data[0])) {\n        ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                      \"non-absolute path \\\"%V\\\" not pushed\", path);\n        return NGX_DECLINED;\n    }\n\n    if (h3c->max_push_id == (uint64_t) -1\n        || h3c->next_push_id > h3c->max_push_id)\n    {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 abort pushes due to max_push_id\");\n        return NGX_ABORT;\n    }\n\n    if (h3c->goaway_push_id != (uint64_t) -1) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 abort pushes due to goaway\");\n        return NGX_ABORT;\n    }\n\n    if (h3c->npushing >= h3scf->max_concurrent_pushes) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 abort pushes due to max_concurrent_pushes\");\n        return NGX_ABORT;\n    }\n\n    if (r->headers_in.server.len == 0) {\n        return NGX_ABORT;\n    }\n\n    push_id = h3c->next_push_id++;\n\n    rc = ngx_http_v3_create_push_request(r, path, push_id);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    cl = ngx_http_v3_create_push_promise(r, path, push_id);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (**ll = cl; **ll; *ll = &(**ll)->next);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_create_push_request(ngx_http_request_t *pr, ngx_str_t *path,\n    uint64_t push_id)\n{\n    ngx_connection_t          *c, *pc;\n    ngx_http_request_t        *r;\n    ngx_http_log_ctx_t        *ctx;\n    ngx_http_connection_t     *hc, *phc;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    pc = pr->connection;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"http3 create push request id:%uL\", push_id);\n\n    c = ngx_http_v3_create_push_stream(pc, push_id);\n    if (c == NULL) {\n        return NGX_ABORT;\n    }\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, 1);\n#endif\n\n    hc = ngx_palloc(c->pool, sizeof(ngx_http_connection_t));\n    if (hc == NULL) {\n        ngx_http_close_connection(c);\n        return NGX_ERROR;\n    }\n\n    phc = ngx_http_quic_get_connection(pc);\n    ngx_memcpy(hc, phc, sizeof(ngx_http_connection_t));\n    c->data = hc;\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 NGX_ERROR;\n    }\n\n    ctx->connection = c;\n    ctx->request = NULL;\n    ctx->current_request = NULL;\n\n    c->log->handler = pc->log->handler;\n    c->log->data = ctx;\n    c->log->action = \"processing pushed request headers\";\n\n    c->log_error = NGX_ERROR_INFO;\n\n    r = ngx_http_create_request(c);\n    if (r == NULL) {\n        ngx_http_close_connection(c);\n        return NGX_ERROR;\n    }\n\n    c->data = r;\n\n    ngx_str_set(&r->http_protocol, \"HTTP/3.0\");\n\n    r->http_version = NGX_HTTP_VERSION_30;\n    r->method_name = ngx_http_core_get_method;\n    r->method = NGX_HTTP_GET;\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_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    if (ngx_list_init(&r->headers_in.headers, r->pool, 4,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n\n    r->schema.data = ngx_pstrdup(r->pool, &pr->schema);\n    if (r->schema.data == NULL) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    r->schema.len = pr->schema.len;\n\n    r->uri_start = ngx_pstrdup(r->pool, path);\n    if (r->uri_start == NULL) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    r->uri_end = r->uri_start + path->len;\n\n    if (ngx_http_parse_uri(r) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_process_request_uri(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_v3_set_push_header(r, \"host\", &pr->headers_in.server)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (pr->headers_in.accept_encoding) {\n        if (ngx_http_v3_set_push_header(r, \"accept-encoding\",\n                                        &pr->headers_in.accept_encoding->value)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (pr->headers_in.accept_language) {\n        if (ngx_http_v3_set_push_header(r, \"accept-language\",\n                                        &pr->headers_in.accept_language->value)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (pr->headers_in.user_agent) {\n        if (ngx_http_v3_set_push_header(r, \"user-agent\",\n                                        &pr->headers_in.user_agent->value)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    c->read->handler = ngx_http_v3_push_request_handler;\n    c->read->handler = ngx_http_v3_push_request_handler;\n\n    ngx_post_event(c->read, &ngx_posted_events);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_set_push_header(ngx_http_request_t *r, const char *name,\n    ngx_str_t *value)\n{\n    u_char                     *p;\n    ngx_table_elt_t            *h;\n    ngx_http_header_t          *hh;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http3 push header \\\"%s\\\": \\\"%V\\\"\", name, value);\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    p = ngx_pnalloc(r->pool, value->len + 1);\n    if (p == NULL) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, value->data, value->len);\n    p[value->len] = '\\0';\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        return NGX_ERROR;\n    }\n\n    h->key.data = (u_char *) name;\n    h->key.len = ngx_strlen(name);\n    h->hash = ngx_hash_key(h->key.data, h->key.len);\n    h->lowcase_key = (u_char *) name;\n    h->value.data = p;\n    h->value.len = value->len;\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\nstatic void\nngx_http_v3_push_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_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 push request handler\");\n\n    ngx_http_process_request(r);\n}\n\n\nstatic ngx_chain_t *\nngx_http_v3_create_push_promise(ngx_http_request_t *r, ngx_str_t *path,\n    uint64_t push_id)\n{\n    size_t                  n, len;\n    ngx_buf_t              *b;\n    ngx_chain_t            *hl, *cl;\n    ngx_http_v3_session_t  *h3c;\n\n    h3c = ngx_http_v3_get_session(r->connection);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http3 create push promise id:%uL\", push_id);\n\n    len = ngx_http_v3_encode_varlen_int(NULL, push_id);\n\n    len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0);\n\n    len += ngx_http_v3_encode_field_ri(NULL, 0,\n                                       NGX_HTTP_V3_HEADER_METHOD_GET);\n\n    len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                        NGX_HTTP_V3_HEADER_AUTHORITY,\n                                        NULL, r->headers_in.server.len);\n\n    if (path->len == 1 && path->data[0] == '/') {\n        len += ngx_http_v3_encode_field_ri(NULL, 0,\n                                           NGX_HTTP_V3_HEADER_PATH_ROOT);\n\n    } else {\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                            NGX_HTTP_V3_HEADER_PATH_ROOT,\n                                            NULL, path->len);\n    }\n\n    if (r->schema.len == 5 && ngx_strncmp(r->schema.data, \"https\", 5) == 0) {\n        len += ngx_http_v3_encode_field_ri(NULL, 0,\n                                           NGX_HTTP_V3_HEADER_SCHEME_HTTPS);\n\n    } else if (r->schema.len == 4\n               && ngx_strncmp(r->schema.data, \"http\", 4) == 0)\n    {\n        len += ngx_http_v3_encode_field_ri(NULL, 0,\n                                           NGX_HTTP_V3_HEADER_SCHEME_HTTP);\n\n    } else {\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                            NGX_HTTP_V3_HEADER_SCHEME_HTTP,\n                                            NULL, r->schema.len);\n    }\n\n    if (r->headers_in.accept_encoding) {\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                     NGX_HTTP_V3_HEADER_ACCEPT_ENCODING, NULL,\n                                     r->headers_in.accept_encoding->value.len);\n    }\n\n    if (r->headers_in.accept_language) {\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                     NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE, NULL,\n                                     r->headers_in.accept_language->value.len);\n    }\n\n    if (r->headers_in.user_agent) {\n        len += ngx_http_v3_encode_field_lri(NULL, 0,\n                                          NGX_HTTP_V3_HEADER_USER_AGENT, NULL,\n                                          r->headers_in.user_agent->value.len);\n    }\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, push_id);\n\n    b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->last,\n                                                                 0, 0, 0);\n\n    b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,\n                                                NGX_HTTP_V3_HEADER_METHOD_GET);\n\n    b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                                  NGX_HTTP_V3_HEADER_AUTHORITY,\n                                                  r->headers_in.server.data,\n                                                  r->headers_in.server.len);\n\n    if (path->len == 1 && path->data[0] == '/') {\n        b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,\n                                                 NGX_HTTP_V3_HEADER_PATH_ROOT);\n\n    } else {\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                                  NGX_HTTP_V3_HEADER_PATH_ROOT,\n                                                  path->data, path->len);\n    }\n\n    if (r->schema.len == 5 && ngx_strncmp(r->schema.data, \"https\", 5) == 0) {\n        b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,\n                                              NGX_HTTP_V3_HEADER_SCHEME_HTTPS);\n\n    } else if (r->schema.len == 4\n               && ngx_strncmp(r->schema.data, \"http\", 4) == 0)\n    {\n        b->last = (u_char *) ngx_http_v3_encode_field_ri(b->last, 0,\n                                               NGX_HTTP_V3_HEADER_SCHEME_HTTP);\n\n    } else {\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                                NGX_HTTP_V3_HEADER_SCHEME_HTTP,\n                                                r->schema.data, r->schema.len);\n    }\n\n    if (r->headers_in.accept_encoding) {\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                     NGX_HTTP_V3_HEADER_ACCEPT_ENCODING,\n                                     r->headers_in.accept_encoding->value.data,\n                                     r->headers_in.accept_encoding->value.len);\n    }\n\n    if (r->headers_in.accept_language) {\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                     NGX_HTTP_V3_HEADER_ACCEPT_LANGUAGE,\n                                     r->headers_in.accept_language->value.data,\n                                     r->headers_in.accept_language->value.len);\n    }\n\n    if (r->headers_in.user_agent) {\n        b->last = (u_char *) ngx_http_v3_encode_field_lri(b->last, 0,\n                                          NGX_HTTP_V3_HEADER_USER_AGENT,\n                                          r->headers_in.user_agent->value.data,\n                                          r->headers_in.user_agent->value.len);\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    n = b->last - b->pos;\n\n    h3c->payload_bytes += n;\n\n    len = ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_FRAME_PUSH_PROMISE)\n          + ngx_http_v3_encode_varlen_int(NULL, n);\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last,\n                                               NGX_HTTP_V3_FRAME_PUSH_PROMISE);\n    b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n);\n\n    hl = ngx_alloc_chain_link(r->pool);\n    if (hl == NULL) {\n        return NULL;\n    }\n\n    hl->buf = b;\n    hl->next = cl;\n\n    return hl;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_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_v3_session_t     *h3c;\n    ngx_http_v3_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_v3_filter_module);\n    if (ctx == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    h3c = ngx_http_v3_get_session(r->connection);\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                       \"http3 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            chunk = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2);\n            if (chunk == NULL) {\n                return NGX_ERROR;\n            }\n\n            b->start = chunk;\n            b->end = chunk + NGX_HTTP_V3_VARLEN_INT_LEN * 2;\n        }\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module;\n        b->memory = 0;\n        b->temporary = 1;\n        b->pos = chunk;\n\n        b->last = (u_char *) ngx_http_v3_encode_varlen_int(chunk,\n                                                       NGX_HTTP_V3_FRAME_DATA);\n        b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, size);\n\n        tl->next = out;\n        out = tl;\n\n        h3c->payload_bytes += size;\n    }\n\n    if (cl->buf->last_buf) {\n        tl = ngx_http_v3_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    } else {\n        *ll = NULL;\n    }\n\n    for (cl = out; cl; cl = cl->next) {\n        h3c->total_bytes += cl->buf->last - cl->buf->pos;\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_v3_filter_module);\n\n    return rc;\n}\n\n\nstatic ngx_chain_t *\nngx_http_v3_create_trailers(ngx_http_request_t *r,\n    ngx_http_v3_filter_ctx_t *ctx)\n{\n    size_t                  len, n;\n    u_char                 *p;\n    ngx_buf_t              *b;\n    ngx_uint_t              i;\n    ngx_chain_t            *cl, *hl;\n    ngx_list_part_t        *part;\n    ngx_table_elt_t        *header;\n    ngx_http_v3_session_t  *h3c;\n\n    h3c = ngx_http_v3_get_session(r->connection);\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 += ngx_http_v3_encode_field_l(NULL, &header[i].key,\n                                          &header[i].value);\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_v3_filter_module;\n    b->memory = 0;\n    b->last_buf = 1;\n\n    if (len == 0) {\n        b->temporary = 0;\n        b->pos = b->last = NULL;\n        return cl;\n    }\n\n    b->temporary = 1;\n\n    len += ngx_http_v3_encode_field_section_prefix(NULL, 0, 0, 0);\n\n    b->pos = ngx_palloc(r->pool, len);\n    if (b->pos == NULL) {\n        return NULL;\n    }\n\n    b->last = (u_char *) ngx_http_v3_encode_field_section_prefix(b->pos,\n                                                                 0, 0, 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        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 output trailer: \\\"%V: %V\\\"\",\n                       &header[i].key, &header[i].value);\n\n        b->last = (u_char *) ngx_http_v3_encode_field_l(b->last,\n                                                        &header[i].key,\n                                                        &header[i].value);\n    }\n\n    n = b->last - b->pos;\n\n    h3c->payload_bytes += n;\n\n    hl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n    if (hl == NULL) {\n        return NULL;\n    }\n\n    b = hl->buf;\n    p = b->start;\n\n    if (p == NULL) {\n        p = ngx_palloc(r->pool, NGX_HTTP_V3_VARLEN_INT_LEN * 2);\n        if (p == NULL) {\n            return NULL;\n        }\n\n        b->start = p;\n        b->end = p + NGX_HTTP_V3_VARLEN_INT_LEN * 2;\n    }\n\n    b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_module;\n    b->memory = 0;\n    b->temporary = 1;\n    b->pos = p;\n\n    b->last = (u_char *) ngx_http_v3_encode_varlen_int(p,\n                                                    NGX_HTTP_V3_FRAME_HEADERS);\n    b->last = (u_char *) ngx_http_v3_encode_varlen_int(b->last, n);\n\n    hl->next = cl;\n\n    return hl;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_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_v3_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_v3_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Roman Arutyunyan\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic void *ngx_http_v3_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\nstatic ngx_command_t  ngx_http_v3_commands[] = {\n\n    { ngx_string(\"http3_max_table_capacity\"),\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_v3_srv_conf_t, max_table_capacity),\n      NULL },\n\n    { ngx_string(\"http3_max_blocked_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_v3_srv_conf_t, max_blocked_streams),\n      NULL },\n\n    { ngx_string(\"http3_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_v3_srv_conf_t, max_concurrent_pushes),\n      NULL },\n\n    { ngx_string(\"http3_max_uni_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_v3_srv_conf_t, max_uni_streams),\n      NULL },\n\n    { ngx_string(\"http3_push\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_v3_push,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"http3_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_v3_loc_conf_t, push_preload),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_v3_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_v3_create_srv_conf,           /* create server configuration */\n    ngx_http_v3_merge_srv_conf,            /* merge server configuration */\n\n    ngx_http_v3_create_loc_conf,           /* create location configuration */\n    ngx_http_v3_merge_loc_conf             /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_v3_module = {\n    NGX_MODULE_V1,\n    &ngx_http_v3_module_ctx,               /* module context */\n    ngx_http_v3_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_v3_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_v3_srv_conf_t  *h3scf;\n\n    h3scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t));\n    if (h3scf == NULL) {\n        return NULL;\n    }\n\n    h3scf->max_table_capacity = NGX_CONF_UNSET_SIZE;\n    h3scf->max_blocked_streams = NGX_CONF_UNSET_UINT;\n    h3scf->max_concurrent_pushes = NGX_CONF_UNSET_UINT;\n    h3scf->max_uni_streams = NGX_CONF_UNSET_UINT;\n\n    return h3scf;\n}\n\n\nstatic char *\nngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_v3_srv_conf_t *prev = parent;\n    ngx_http_v3_srv_conf_t *conf = child;\n\n    ngx_conf_merge_size_value(conf->max_table_capacity,\n                              prev->max_table_capacity, 16384);\n\n    ngx_conf_merge_uint_value(conf->max_blocked_streams,\n                              prev->max_blocked_streams, 16);\n\n    ngx_conf_merge_uint_value(conf->max_concurrent_pushes,\n                              prev->max_concurrent_pushes, 10);\n\n    ngx_conf_merge_uint_value(conf->max_uni_streams,\n                              prev->max_uni_streams, 3);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_v3_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_v3_loc_conf_t  *h3lcf;\n\n    h3lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_loc_conf_t));\n    if (h3lcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     h3lcf->pushes = NULL;\n     */\n\n    h3lcf->push_preload = NGX_CONF_UNSET;\n    h3lcf->push = NGX_CONF_UNSET;\n\n    return h3lcf;\n}\n\n\nstatic char *\nngx_http_v3_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_v3_loc_conf_t *prev = parent;\n    ngx_http_v3_loc_conf_t *conf = child;\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_v3_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_v3_loc_conf_t *h3lcf = 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 (h3lcf->pushes) {\n            return \"\\\"off\\\" parameter cannot be used with URI\";\n        }\n\n        if (h3lcf->push == 0) {\n            return \"is duplicate\";\n        }\n\n        h3lcf->push = 0;\n        return NGX_CONF_OK;\n    }\n\n    if (h3lcf->push == 0) {\n        return \"URI cannot be used with \\\"off\\\" parameter\";\n    }\n\n    h3lcf->push = 1;\n\n    if (h3lcf->pushes == NULL) {\n        h3lcf->pushes = ngx_array_create(cf->pool, 1,\n                                         sizeof(ngx_http_complex_value_t));\n        if (h3lcf->pushes == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    cv = ngx_array_push(h3lcf->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"
  },
  {
    "path": "src/http/v3/ngx_http_v3_parse.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\n#define ngx_http_v3_is_v2_frame(type)                                         \\\n    ((type) == 0x02 || (type) == 0x06 || (type) == 0x08 || (type) == 0x09)\n\n\nstatic void ngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc,\n    ngx_uint_t n);\nstatic void ngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc,\n    ngx_uint_t *n);\nstatic ngx_int_t ngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length);\n\nstatic ngx_int_t ngx_http_v3_parse_varlen_int(ngx_connection_t *c,\n    ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_prefix_int(ngx_connection_t *c,\n    ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b);\n\nstatic ngx_int_t ngx_http_v3_parse_field_section_prefix(ngx_connection_t *c,\n    ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_rep(ngx_connection_t *c,\n    ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_literal(ngx_connection_t *c,\n    ngx_http_v3_parse_literal_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_ri(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_lri(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_l(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_pbi(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_lpbi(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);\n\nstatic ngx_int_t ngx_http_v3_parse_control(ngx_connection_t *c,\n    ngx_http_v3_parse_control_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_settings(ngx_connection_t *c,\n    ngx_http_v3_parse_settings_t *st, ngx_buf_t *b);\n\nstatic ngx_int_t ngx_http_v3_parse_encoder(ngx_connection_t *c,\n    ngx_http_v3_parse_encoder_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_inr(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_v3_parse_field_iln(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b);\n\nstatic ngx_int_t ngx_http_v3_parse_decoder(ngx_connection_t *c,\n    ngx_http_v3_parse_decoder_t *st, ngx_buf_t *b);\n\nstatic ngx_int_t ngx_http_v3_parse_lookup(ngx_connection_t *c,\n    ngx_uint_t dynamic, ngx_uint_t index, ngx_str_t *name, ngx_str_t *value);\n\n\nstatic void\nngx_http_v3_parse_start_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t n)\n{\n    *loc = *b;\n\n    if ((size_t) (loc->last - loc->pos) > n) {\n        loc->last = loc->pos + n;\n    }\n}\n\n\nstatic void\nngx_http_v3_parse_end_local(ngx_buf_t *b, ngx_buf_t *loc, ngx_uint_t *pn)\n{\n    *pn -= loc->pos - b->pos;\n    b->pos = loc->pos;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_skip(ngx_buf_t *b, ngx_uint_t *length)\n{\n    if ((size_t) (b->last - b->pos) < *length) {\n        *length -= b->last - b->pos;\n        b->pos = b->last;\n        return NGX_AGAIN;\n    }\n\n    b->pos += *length;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_varlen_int(ngx_connection_t *c,\n    ngx_http_v3_parse_varlen_int_t *st, ngx_buf_t *b)\n{\n    u_char  ch;\n    enum {\n        sw_start = 0,\n        sw_length_2,\n        sw_length_3,\n        sw_length_4,\n        sw_length_5,\n        sw_length_6,\n        sw_length_7,\n        sw_length_8\n    };\n\n    for ( ;; ) {\n\n        if (b->pos == b->last) {\n            return NGX_AGAIN;\n        }\n\n        ch = *b->pos++;\n\n        switch (st->state) {\n\n        case sw_start:\n\n            st->value = ch;\n            if (st->value & 0xc0) {\n                st->state = sw_length_2;\n                break;\n            }\n\n            goto done;\n\n        case sw_length_2:\n\n            st->value = (st->value << 8) + ch;\n            if ((st->value & 0xc000) == 0x4000) {\n                st->value &= 0x3fff;\n                goto done;\n            }\n\n            st->state = sw_length_3;\n            break;\n\n        case sw_length_4:\n\n            st->value = (st->value << 8) + ch;\n            if ((st->value & 0xc0000000) == 0x80000000) {\n                st->value &= 0x3fffffff;\n                goto done;\n            }\n\n            st->state = sw_length_5;\n            break;\n\n        case sw_length_3:\n        case sw_length_5:\n        case sw_length_6:\n        case sw_length_7:\n\n            st->value = (st->value << 8) + ch;\n            st->state++;\n            break;\n\n        case sw_length_8:\n\n            st->value = (st->value << 8) + ch;\n            st->value &= 0x3fffffffffffffff;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse varlen int %uL\", st->value);\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_prefix_int(ngx_connection_t *c,\n    ngx_http_v3_parse_prefix_int_t *st, ngx_uint_t prefix, ngx_buf_t *b)\n{\n    u_char      ch;\n    ngx_uint_t  mask;\n    enum {\n        sw_start = 0,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        if (b->pos == b->last) {\n            return NGX_AGAIN;\n        }\n\n        ch = *b->pos++;\n\n        switch (st->state) {\n\n        case sw_start:\n\n            mask = (1 << prefix) - 1;\n            st->value = ch & mask;\n\n            if (st->value != mask) {\n                goto done;\n            }\n\n            st->shift = 0;\n            st->state = sw_value;\n            break;\n\n        case sw_value:\n\n            st->value += (uint64_t) (ch & 0x7f) << st->shift;\n\n            if (st->shift == 56\n                && ((ch & 0x80) || (st->value & 0xc000000000000000)))\n            {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client exceeded integer size limit\");\n                return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD;\n            }\n\n            if (ch & 0x80) {\n                st->shift += 7;\n                break;\n            }\n\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse prefix int %uL\", st->value);\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nngx_int_t\nngx_http_v3_parse_headers(ngx_connection_t *c, ngx_http_v3_parse_headers_t *st,\n    ngx_buf_t *b)\n{\n    ngx_buf_t  loc;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_type,\n        sw_length,\n        sw_skip,\n        sw_prefix,\n        sw_verify,\n        sw_field_rep,\n        sw_done\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse headers\");\n\n            st->state = sw_type;\n\n            /* fall through */\n\n        case sw_type:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->type = st->vlint.value;\n\n            if (ngx_http_v3_is_v2_frame(st->type)\n                || st->type == NGX_HTTP_V3_FRAME_DATA\n                || st->type == NGX_HTTP_V3_FRAME_GOAWAY\n                || st->type == NGX_HTTP_V3_FRAME_SETTINGS\n                || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID\n                || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH\n                || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)\n            {\n                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;\n            }\n\n            st->state = sw_length;\n            break;\n\n        case sw_length:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->length = st->vlint.value;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse headers type:%ui, len:%ui\",\n                           st->type, st->length);\n\n            if (st->type != NGX_HTTP_V3_FRAME_HEADERS) {\n                st->state = st->length > 0 ? sw_skip : sw_type;\n                break;\n            }\n\n            if (st->length == 0) {\n                return NGX_HTTP_V3_ERR_FRAME_ERROR;\n            }\n\n            st->state = sw_prefix;\n            break;\n\n        case sw_skip:\n\n            rc = ngx_http_v3_parse_skip(b, &st->length);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->state = sw_type;\n            break;\n\n        case sw_prefix:\n\n            ngx_http_v3_parse_start_local(b, &loc, st->length);\n\n            rc = ngx_http_v3_parse_field_section_prefix(c, &st->prefix, &loc);\n\n            ngx_http_v3_parse_end_local(b, &loc, &st->length);\n\n            if (st->length == 0 && rc == NGX_AGAIN) {\n                return NGX_HTTP_V3_ERR_FRAME_ERROR;\n            }\n\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->state = sw_verify;\n            break;\n\n        case sw_verify:\n\n            rc = ngx_http_v3_check_insert_count(c, st->prefix.insert_count);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_field_rep;\n\n            /* fall through */\n\n        case sw_field_rep:\n\n            ngx_http_v3_parse_start_local(b, &loc, st->length);\n\n            rc = ngx_http_v3_parse_field_rep(c, &st->field_rep, st->prefix.base,\n                                             &loc);\n\n            ngx_http_v3_parse_end_local(b, &loc, &st->length);\n\n            if (st->length == 0 && rc == NGX_AGAIN) {\n                return NGX_HTTP_V3_ERR_FRAME_ERROR;\n            }\n\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            if (st->length == 0) {\n                goto done;\n            }\n\n            return NGX_OK;\n        }\n    }\n\ndone:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 parse headers done\");\n\n    if (st->prefix.insert_count > 0) {\n        if (ngx_http_v3_send_ack_section(c, c->quic->id) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_section_prefix(ngx_connection_t *c,\n    ngx_http_v3_parse_field_section_prefix_t *st, ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_req_insert_count,\n        sw_delta_base,\n        sw_read_delta_base\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse field section prefix\");\n\n            st->state = sw_req_insert_count;\n\n            /* fall through */\n\n        case sw_req_insert_count:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 8, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->insert_count = st->pint.value;\n            st->state = sw_delta_base;\n            break;\n\n        case sw_delta_base:\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->sign = (ch & 0x80) ? 1 : 0;\n            st->state = sw_read_delta_base;\n\n            /* fall through */\n\n        case sw_read_delta_base:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->delta_base = st->pint.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    rc = ngx_http_v3_decode_insert_count(c, &st->insert_count);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    if (st->sign) {\n        st->base = st->insert_count - st->delta_base - 1;\n    } else {\n        st->base = st->insert_count + st->delta_base;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                  \"http3 parse field section prefix done \"\n                  \"insert_count:%ui, sign:%ui, delta_base:%ui, base:%ui\",\n                  st->insert_count, st->sign, st->delta_base, st->base);\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_rep(ngx_connection_t *c,\n    ngx_http_v3_parse_field_rep_t *st, ngx_uint_t base, ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_field_ri,\n        sw_field_lri,\n        sw_field_l,\n        sw_field_pbi,\n        sw_field_lpbi\n    };\n\n    if (st->state == sw_start) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 parse field representation\");\n\n        if (b->pos == b->last) {\n            return NGX_AGAIN;\n        }\n\n        ch = *b->pos;\n\n        ngx_memzero(&st->field, sizeof(ngx_http_v3_parse_field_t));\n\n        st->field.base = base;\n\n        if (ch & 0x80) {\n            /* Indexed Field Line */\n\n            st->state = sw_field_ri;\n\n        } else if (ch & 0x40) {\n            /* Literal Field Line With Name Reference */\n\n            st->state = sw_field_lri;\n\n        } else if (ch & 0x20) {\n            /* Literal Field Line With Literal Name */\n\n            st->state = sw_field_l;\n\n        } else if (ch & 0x10) {\n            /* Indexed Field Line With Post-Base Index */\n\n            st->state = sw_field_pbi;\n\n        } else {\n            /* Literal Field Line With Post-Base Name Reference */\n\n            st->state = sw_field_lpbi;\n        }\n    }\n\n    switch (st->state) {\n\n    case sw_field_ri:\n        rc = ngx_http_v3_parse_field_ri(c, &st->field, b);\n        break;\n\n    case sw_field_lri:\n        rc = ngx_http_v3_parse_field_lri(c, &st->field, b);\n        break;\n\n    case sw_field_l:\n        rc = ngx_http_v3_parse_field_l(c, &st->field, b);\n        break;\n\n    case sw_field_pbi:\n        rc = ngx_http_v3_parse_field_pbi(c, &st->field, b);\n        break;\n\n    case sw_field_lpbi:\n        rc = ngx_http_v3_parse_field_lpbi(c, &st->field, b);\n        break;\n\n    default:\n        rc = NGX_OK;\n    }\n\n    if (rc != NGX_DONE) {\n        return rc;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field representation done\");\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_literal(ngx_connection_t *c, ngx_http_v3_parse_literal_t *st,\n    ngx_buf_t *b)\n{\n    u_char                     ch;\n    ngx_uint_t                 n;\n    ngx_http_core_srv_conf_t  *cscf;\n    enum {\n        sw_start = 0,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse literal huff:%ui, len:%ui\",\n                           st->huffman, st->length);\n\n            n = st->length;\n\n            cscf = ngx_http_v3_get_module_srv_conf(c, ngx_http_core_module);\n\n            if (n > cscf->large_client_header_buffers.size) {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client sent too large field line\");\n                return NGX_HTTP_V3_ERR_EXCESSIVE_LOAD;\n            }\n\n            if (st->huffman) {\n                n = n * 8 / 5;\n                st->huffstate = 0;\n            }\n\n            st->last = ngx_pnalloc(c->pool, n + 1);\n            if (st->last == NULL) {\n                return NGX_ERROR;\n            }\n\n            st->value.data = st->last;\n            st->state = sw_value;\n\n            /* fall through */\n\n        case sw_value:\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos++;\n\n            if (st->huffman) {\n                if (ngx_http_v2_huff_decode(&st->huffstate, &ch, 1, &st->last,\n                                            st->length == 1, c->log)\n                    != NGX_OK)\n                {\n                    return NGX_ERROR;\n                }\n\n            } else {\n                *st->last++ = ch;\n            }\n\n            if (--st->length) {\n                break;\n            }\n\n            st->value.len = st->last - st->value.data;\n            *st->last = '\\0';\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse literal done \\\"%V\\\"\", &st->value);\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_ri(ngx_connection_t *c, ngx_http_v3_parse_field_t *st,\n    ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_index\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse field ri\");\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->dynamic = (ch & 0x40) ? 0 : 1;\n            st->state = sw_index;\n\n            /* fall through */\n\n        case sw_index:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->index = st->pint.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field ri done %s%ui]\",\n                   st->dynamic ? \"dynamic[-\" : \"static[\", st->index);\n\n    if (st->dynamic) {\n        st->index = st->base - st->index - 1;\n    }\n\n    rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name,\n                                  &st->value);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_lri(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_index,\n        sw_value_len,\n        sw_read_value_len,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse field lri\");\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->dynamic = (ch & 0x10) ? 0 : 1;\n            st->state = sw_index;\n\n            /* fall through */\n\n        case sw_index:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->index = st->pint.value;\n            st->state = sw_value_len;\n            break;\n\n        case sw_value_len:\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->literal.huffman = (ch & 0x80) ? 1 : 0;\n            st->state = sw_read_value_len;\n\n            /* fall through */\n\n        case sw_read_value_len:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->literal.length = st->pint.value;\n            if (st->literal.length == 0) {\n                goto done;\n            }\n\n            st->state = sw_value;\n            break;\n\n        case sw_value:\n\n            rc = ngx_http_v3_parse_literal(c, &st->literal, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->value = st->literal.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field lri done %s%ui] \\\"%V\\\"\",\n                   st->dynamic ? \"dynamic[-\" : \"static[\",\n                   st->index, &st->value);\n\n    if (st->dynamic) {\n        st->index = st->base - st->index - 1;\n    }\n\n    rc = ngx_http_v3_parse_lookup(c, st->dynamic, st->index, &st->name, NULL);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_l(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_name_len,\n        sw_name,\n        sw_value_len,\n        sw_read_value_len,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 parse field l\");\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->literal.huffman = (ch & 0x08) ? 1 : 0;\n            st->state = sw_name_len;\n\n            /* fall through */\n\n        case sw_name_len:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->literal.length = st->pint.value;\n            if (st->literal.length == 0) {\n                return NGX_ERROR;\n            }\n\n            st->state = sw_name;\n            break;\n\n        case sw_name:\n\n            rc = ngx_http_v3_parse_literal(c, &st->literal, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->name = st->literal.value;\n            st->state = sw_value_len;\n            break;\n\n        case sw_value_len:\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->literal.huffman = (ch & 0x80) ? 1 : 0;\n            st->state = sw_read_value_len;\n\n            /* fall through */\n\n        case sw_read_value_len:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->literal.length = st->pint.value;\n            if (st->literal.length == 0) {\n                goto done;\n            }\n\n            st->state = sw_value;\n            break;\n\n        case sw_value:\n\n            rc = ngx_http_v3_parse_literal(c, &st->literal, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->value = st->literal.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field l done \\\"%V\\\" \\\"%V\\\"\",\n                   &st->name, &st->value);\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_pbi(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)\n{\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_index\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse field pbi\");\n\n            st->state = sw_index;\n\n            /* fall through */\n\n        case sw_index:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 4, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->index = st->pint.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field pbi done dynamic[+%ui]\", st->index);\n\n    rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name,\n                                  &st->value);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_lpbi(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_index,\n        sw_value_len,\n        sw_read_value_len,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse field lpbi\");\n\n            st->state = sw_index;\n\n            /* fall through */\n\n        case sw_index:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 3, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->index = st->pint.value;\n            st->state = sw_value_len;\n            break;\n\n        case sw_value_len:\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->literal.huffman = (ch & 0x80) ? 1 : 0;\n            st->state = sw_read_value_len;\n\n            /* fall through */\n\n        case sw_read_value_len:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->literal.length = st->pint.value;\n            if (st->literal.length == 0) {\n                goto done;\n            }\n\n            st->state = sw_value;\n            break;\n\n        case sw_value:\n\n            rc = ngx_http_v3_parse_literal(c, &st->literal, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->value = st->literal.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field lpbi done dynamic[+%ui] \\\"%V\\\"\",\n                   st->index, &st->value);\n\n    rc = ngx_http_v3_parse_lookup(c, 1, st->base + st->index, &st->name, NULL);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_lookup(ngx_connection_t *c, ngx_uint_t dynamic,\n    ngx_uint_t index, ngx_str_t *name, ngx_str_t *value)\n{\n    u_char  *p;\n\n    if (!dynamic) {\n        if (ngx_http_v3_lookup_static(c, index, name, value) != NGX_OK) {\n            return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;\n        }\n\n        return NGX_OK;\n    }\n\n    if (ngx_http_v3_lookup(c, index, name, value) != NGX_OK) {\n        return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;\n    }\n\n    if (name) {\n        p = ngx_pnalloc(c->pool, name->len + 1);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(p, name->data, name->len);\n        p[name->len] = '\\0';\n        name->data = p;\n    }\n\n    if (value) {\n        p = ngx_pnalloc(c->pool, value->len + 1);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(p, value->data, value->len);\n        p[value->len] = '\\0';\n        value->data = p;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_control(ngx_connection_t *c, ngx_http_v3_parse_control_t *st,\n    ngx_buf_t *b)\n{\n    ngx_buf_t  loc;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_first_type,\n        sw_type,\n        sw_length,\n        sw_cancel_push,\n        sw_settings,\n        sw_max_push_id,\n        sw_goaway,\n        sw_skip\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse control\");\n\n            st->state = sw_first_type;\n\n            /* fall through */\n\n        case sw_first_type:\n        case sw_type:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->type = st->vlint.value;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse frame type:%ui\", st->type);\n\n            if (st->state == sw_first_type\n                && st->type != NGX_HTTP_V3_FRAME_SETTINGS)\n            {\n                return NGX_HTTP_V3_ERR_MISSING_SETTINGS;\n            }\n\n            if (st->state != sw_first_type\n                && st->type == NGX_HTTP_V3_FRAME_SETTINGS)\n            {\n                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;\n            }\n\n            if (ngx_http_v3_is_v2_frame(st->type)\n                || st->type == NGX_HTTP_V3_FRAME_DATA\n                || st->type == NGX_HTTP_V3_FRAME_HEADERS\n                || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)\n            {\n                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;\n            }\n\n            st->state = sw_length;\n            break;\n\n        case sw_length:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse frame len:%uL\", st->vlint.value);\n\n            st->length = st->vlint.value;\n            if (st->length == 0) {\n                st->state = sw_type;\n                break;\n            }\n\n            switch (st->type) {\n\n            case NGX_HTTP_V3_FRAME_CANCEL_PUSH:\n                st->state = sw_cancel_push;\n                break;\n\n            case NGX_HTTP_V3_FRAME_SETTINGS:\n                st->state = sw_settings;\n                break;\n\n            case NGX_HTTP_V3_FRAME_MAX_PUSH_ID:\n                st->state = sw_max_push_id;\n                break;\n\n            case NGX_HTTP_V3_FRAME_GOAWAY:\n                st->state = sw_goaway;\n                break;\n\n            default:\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                               \"http3 parse skip unknown frame\");\n                st->state = sw_skip;\n            }\n\n            break;\n\n        case sw_cancel_push:\n\n            ngx_http_v3_parse_start_local(b, &loc, st->length);\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc);\n\n            ngx_http_v3_parse_end_local(b, &loc, &st->length);\n\n            if (st->length == 0 && rc == NGX_AGAIN) {\n                return NGX_HTTP_V3_ERR_FRAME_ERROR;\n            }\n\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_cancel_push(c, st->vlint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_type;\n            break;\n\n        case sw_settings:\n\n            ngx_http_v3_parse_start_local(b, &loc, st->length);\n\n            rc = ngx_http_v3_parse_settings(c, &st->settings, &loc);\n\n            ngx_http_v3_parse_end_local(b, &loc, &st->length);\n\n            if (st->length == 0 && rc == NGX_AGAIN) {\n                return NGX_HTTP_V3_ERR_SETTINGS_ERROR;\n            }\n\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            if (st->length == 0) {\n                st->state = sw_type;\n            }\n\n            break;\n\n        case sw_max_push_id:\n\n            ngx_http_v3_parse_start_local(b, &loc, st->length);\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc);\n\n            ngx_http_v3_parse_end_local(b, &loc, &st->length);\n\n            if (st->length == 0 && rc == NGX_AGAIN) {\n                return NGX_HTTP_V3_ERR_FRAME_ERROR;\n            }\n\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_set_max_push_id(c, st->vlint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_type;\n            break;\n\n        case sw_goaway:\n\n            ngx_http_v3_parse_start_local(b, &loc, st->length);\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, &loc);\n\n            ngx_http_v3_parse_end_local(b, &loc, &st->length);\n\n            if (st->length == 0 && rc == NGX_AGAIN) {\n                return NGX_HTTP_V3_ERR_FRAME_ERROR;\n            }\n\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_goaway(c, st->vlint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_type;\n            break;\n\n        case sw_skip:\n\n            rc = ngx_http_v3_parse_skip(b, &st->length);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->state = sw_type;\n            break;\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_settings(ngx_connection_t *c,\n    ngx_http_v3_parse_settings_t *st, ngx_buf_t *b)\n{\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_id,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse settings\");\n\n            st->state = sw_id;\n\n            /* fall through */\n\n        case sw_id:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->id = st->vlint.value;\n            st->state = sw_value;\n            break;\n\n        case sw_value:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            if (ngx_http_v3_set_param(c, st->id, st->vlint.value) != NGX_OK) {\n                return NGX_HTTP_V3_ERR_SETTINGS_ERROR;\n            }\n\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 parse settings done\");\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_encoder(ngx_connection_t *c, ngx_http_v3_parse_encoder_t *st,\n    ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_inr,\n        sw_iln,\n        sw_capacity,\n        sw_duplicate\n    };\n\n    for ( ;; ) {\n\n        if (st->state == sw_start) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse encoder instruction\");\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            if (ch & 0x80) {\n                /* Insert With Name Reference */\n\n                st->state = sw_inr;\n\n            } else if (ch & 0x40) {\n                /* Insert With Literal Name */\n\n                st->state = sw_iln;\n\n            } else if (ch & 0x20) {\n                /* Set Dynamic Table Capacity */\n\n                st->state = sw_capacity;\n\n            } else {\n                /* Duplicate */\n\n                st->state = sw_duplicate;\n            }\n        }\n\n        switch (st->state) {\n\n        case sw_inr:\n\n            rc = ngx_http_v3_parse_field_inr(c, &st->field, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->state = sw_start;\n            break;\n\n        case sw_iln:\n\n            rc = ngx_http_v3_parse_field_iln(c, &st->field, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->state = sw_start;\n            break;\n\n        case sw_capacity:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_set_capacity(c, st->pint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_start;\n            break;\n\n        default: /* sw_duplicate */\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_duplicate(c, st->pint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_start;\n            break;\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_inr(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_name_index,\n        sw_value_len,\n        sw_read_value_len,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse field inr\");\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->dynamic = (ch & 0x40) ? 0 : 1;\n            st->state = sw_name_index;\n\n            /* fall through */\n\n        case sw_name_index:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->index = st->pint.value;\n            st->state = sw_value_len;\n            break;\n\n        case sw_value_len:\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->literal.huffman = (ch & 0x80) ? 1 : 0;\n            st->state = sw_read_value_len;\n\n            /* fall through */\n\n        case sw_read_value_len:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->literal.length = st->pint.value;\n            if (st->literal.length == 0) {\n                st->value.len = 0;\n                goto done;\n            }\n\n            st->state = sw_value;\n            break;\n\n        case sw_value:\n\n            rc = ngx_http_v3_parse_literal(c, &st->literal, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->value = st->literal.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field inr done %s[%ui] \\\"%V\\\"\",\n                   st->dynamic ? \"dynamic\" : \"static\",\n                   st->index, &st->value);\n\n    rc = ngx_http_v3_ref_insert(c, st->dynamic, st->index, &st->value);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_field_iln(ngx_connection_t *c,\n    ngx_http_v3_parse_field_t *st, ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_name_len,\n        sw_name,\n        sw_value_len,\n        sw_read_value_len,\n        sw_value\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse field iln\");\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->literal.huffman = (ch & 0x20) ? 1 : 0;\n            st->state = sw_name_len;\n\n            /* fall through */\n\n        case sw_name_len:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 5, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->literal.length = st->pint.value;\n            if (st->literal.length == 0) {\n                return NGX_ERROR;\n            }\n\n            st->state = sw_name;\n            break;\n\n        case sw_name:\n\n            rc = ngx_http_v3_parse_literal(c, &st->literal, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->name = st->literal.value;\n            st->state = sw_value_len;\n            break;\n\n        case sw_value_len:\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            st->literal.huffman = (ch & 0x80) ? 1 : 0;\n            st->state = sw_read_value_len;\n\n            /* fall through */\n\n        case sw_read_value_len:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->literal.length = st->pint.value;\n            if (st->literal.length == 0) {\n                st->value.len = 0;\n                goto done;\n            }\n\n            st->state = sw_value;\n            break;\n\n        case sw_value:\n\n            rc = ngx_http_v3_parse_literal(c, &st->literal, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->value = st->literal.value;\n            goto done;\n        }\n    }\n\ndone:\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 parse field iln done \\\"%V\\\":\\\"%V\\\"\",\n                   &st->name, &st->value);\n\n    rc = ngx_http_v3_insert(c, &st->name, &st->value);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_decoder(ngx_connection_t *c, ngx_http_v3_parse_decoder_t *st,\n    ngx_buf_t *b)\n{\n    u_char     ch;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_ack_section,\n        sw_cancel_stream,\n        sw_inc_insert_count\n    };\n\n    for ( ;; ) {\n\n        if (st->state == sw_start) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse decoder instruction\");\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ch = *b->pos;\n\n            if (ch & 0x80) {\n                /* Section Acknowledgment */\n\n                st->state = sw_ack_section;\n\n            } else if (ch & 0x40) {\n                /*  Stream Cancellation */\n\n                st->state = sw_cancel_stream;\n\n            }  else {\n                /*  Insert Count Increment */\n\n                st->state = sw_inc_insert_count;\n            }\n        }\n\n        switch (st->state) {\n\n        case sw_ack_section:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 7, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_ack_section(c, st->pint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_start;\n            break;\n\n        case sw_cancel_stream:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_cancel_stream(c, st->pint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_start;\n            break;\n\n        case sw_inc_insert_count:\n\n            rc = ngx_http_v3_parse_prefix_int(c, &st->pint, 6, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_inc_insert_count(c, st->pint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            st->state = sw_start;\n            break;\n        }\n    }\n}\n\n\nngx_int_t\nngx_http_v3_parse_data(ngx_connection_t *c, ngx_http_v3_parse_data_t *st,\n    ngx_buf_t *b)\n{\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_type,\n        sw_length,\n        sw_skip\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 parse data\");\n\n            st->state = sw_type;\n\n            /* fall through */\n\n        case sw_type:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->type = st->vlint.value;\n\n            if (st->type == NGX_HTTP_V3_FRAME_HEADERS) {\n                /* trailers */\n                goto done;\n            }\n\n            if (ngx_http_v3_is_v2_frame(st->type)\n                || st->type == NGX_HTTP_V3_FRAME_GOAWAY\n                || st->type == NGX_HTTP_V3_FRAME_SETTINGS\n                || st->type == NGX_HTTP_V3_FRAME_MAX_PUSH_ID\n                || st->type == NGX_HTTP_V3_FRAME_CANCEL_PUSH\n                || st->type == NGX_HTTP_V3_FRAME_PUSH_PROMISE)\n            {\n                return NGX_HTTP_V3_ERR_FRAME_UNEXPECTED;\n            }\n\n            st->state = sw_length;\n            break;\n\n        case sw_length:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->length = st->vlint.value;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 parse data type:%ui, len:%ui\",\n                           st->type, st->length);\n\n            if (st->type != NGX_HTTP_V3_FRAME_DATA && st->length > 0) {\n                st->state = sw_skip;\n                break;\n            }\n\n            st->state = sw_type;\n            return NGX_OK;\n\n        case sw_skip:\n\n            rc = ngx_http_v3_parse_skip(b, &st->length);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            st->state = sw_type;\n            break;\n        }\n    }\n\ndone:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 parse data done\");\n\n    st->state = sw_start;\n    return NGX_DONE;\n}\n\n\nngx_int_t\nngx_http_v3_parse_uni(ngx_connection_t *c, ngx_http_v3_parse_uni_t *st,\n    ngx_buf_t *b)\n{\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_type,\n        sw_control,\n        sw_encoder,\n        sw_decoder,\n        sw_unknown\n    };\n\n    for ( ;; ) {\n\n        switch (st->state) {\n        case sw_start:\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 parse uni\");\n\n            st->state = sw_type;\n\n            /* fall through */\n\n        case sw_type:\n\n            rc = ngx_http_v3_parse_varlen_int(c, &st->vlint, b);\n            if (rc != NGX_DONE) {\n                return rc;\n            }\n\n            rc = ngx_http_v3_register_uni_stream(c, st->vlint.value);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            switch (st->vlint.value) {\n            case NGX_HTTP_V3_STREAM_CONTROL:\n                st->state = sw_control;\n                break;\n\n            case NGX_HTTP_V3_STREAM_ENCODER:\n                st->state = sw_encoder;\n                break;\n\n            case NGX_HTTP_V3_STREAM_DECODER:\n                st->state = sw_decoder;\n                break;\n\n            default:\n                st->state = sw_unknown;\n            }\n\n            break;\n\n        case sw_control:\n\n            return ngx_http_v3_parse_control(c, &st->u.control, b);\n\n        case sw_encoder:\n\n            return ngx_http_v3_parse_encoder(c, &st->u.encoder, b);\n\n        case sw_decoder:\n\n            return ngx_http_v3_parse_decoder(c, &st->u.decoder, b);\n\n        case sw_unknown:\n\n            b->pos = b->last;\n            return NGX_AGAIN;\n        }\n    }\n}\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_parse.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_V3_PARSE_H_INCLUDED_\n#define _NGX_HTTP_V3_PARSE_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_uint_t                      state;\n    uint64_t                        value;\n} ngx_http_v3_parse_varlen_int_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_uint_t                      shift;\n    uint64_t                        value;\n} ngx_http_v3_parse_prefix_int_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    uint64_t                        id;\n    ngx_http_v3_parse_varlen_int_t  vlint;\n} ngx_http_v3_parse_settings_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_uint_t                      insert_count;\n    ngx_uint_t                      delta_base;\n    ngx_uint_t                      sign;\n    ngx_uint_t                      base;\n    ngx_http_v3_parse_prefix_int_t  pint;\n} ngx_http_v3_parse_field_section_prefix_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_uint_t                      length;\n    ngx_uint_t                      huffman;\n    ngx_str_t                       value;\n    u_char                         *last;\n    u_char                          huffstate;\n} ngx_http_v3_parse_literal_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_uint_t                      index;\n    ngx_uint_t                      base;\n    ngx_uint_t                      dynamic;\n\n    ngx_str_t                       name;\n    ngx_str_t                       value;\n\n    ngx_http_v3_parse_prefix_int_t  pint;\n    ngx_http_v3_parse_literal_t     literal;\n} ngx_http_v3_parse_field_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_http_v3_parse_field_t       field;\n} ngx_http_v3_parse_field_rep_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_uint_t                      type;\n    ngx_uint_t                      length;\n    ngx_http_v3_parse_varlen_int_t  vlint;\n    ngx_http_v3_parse_field_section_prefix_t  prefix;\n    ngx_http_v3_parse_field_rep_t   field_rep;\n} ngx_http_v3_parse_headers_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_http_v3_parse_field_t       field;\n    ngx_http_v3_parse_prefix_int_t  pint;\n} ngx_http_v3_parse_encoder_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_http_v3_parse_prefix_int_t  pint;\n} ngx_http_v3_parse_decoder_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_uint_t                      type;\n    ngx_uint_t                      length;\n    ngx_http_v3_parse_varlen_int_t  vlint;\n    ngx_http_v3_parse_settings_t    settings;\n} ngx_http_v3_parse_control_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_http_v3_parse_varlen_int_t  vlint;\n    union {\n        ngx_http_v3_parse_encoder_t  encoder;\n        ngx_http_v3_parse_decoder_t  decoder;\n        ngx_http_v3_parse_control_t  control;\n    } u;\n} ngx_http_v3_parse_uni_t;\n\n\ntypedef struct {\n    ngx_uint_t                      state;\n    ngx_uint_t                      type;\n    ngx_uint_t                      length;\n    ngx_http_v3_parse_varlen_int_t  vlint;\n} ngx_http_v3_parse_data_t;\n\n\n/*\n * Parse functions return codes:\n *   NGX_DONE - parsing done\n *   NGX_OK - sub-element done\n *   NGX_AGAIN - more data expected\n *   NGX_BUSY - waiting for external event\n *   NGX_ERROR - internal error\n *   NGX_HTTP_V3_ERROR_XXX - HTTP/3 or QPACK error\n */\n\nngx_int_t ngx_http_v3_parse_headers(ngx_connection_t *c,\n    ngx_http_v3_parse_headers_t *st, ngx_buf_t *b);\nngx_int_t ngx_http_v3_parse_data(ngx_connection_t *c,\n    ngx_http_v3_parse_data_t *st, ngx_buf_t *b);\nngx_int_t ngx_http_v3_parse_uni(ngx_connection_t *c,\n    ngx_http_v3_parse_uni_t *st, ngx_buf_t *b);\n\n\n#endif /* _NGX_HTTP_V3_PARSE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_request.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\nstatic void ngx_http_v3_wait_request_handler(ngx_event_t *rev);\nstatic void ngx_http_v3_cleanup_request(void *data);\nstatic void ngx_http_v3_process_request(ngx_event_t *rev);\nstatic ngx_int_t ngx_http_v3_process_header(ngx_http_request_t *r,\n    ngx_str_t *name, ngx_str_t *value);\nstatic ngx_int_t ngx_http_v3_validate_header(ngx_http_request_t *r,\n    ngx_str_t *name, ngx_str_t *value);\nstatic ngx_int_t ngx_http_v3_process_pseudo_header(ngx_http_request_t *r,\n    ngx_str_t *name, ngx_str_t *value);\nstatic ngx_int_t ngx_http_v3_init_pseudo_headers(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_v3_process_request_header(ngx_http_request_t *r);\nstatic void ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_v3_do_read_client_request_body(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_v3_request_body_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\n\n\nstatic const struct {\n    ngx_str_t   name;\n    ngx_uint_t  method;\n} ngx_http_v3_methods[] = {\n\n    { ngx_string(\"GET\"),       NGX_HTTP_GET },\n    { ngx_string(\"POST\"),      NGX_HTTP_POST },\n    { ngx_string(\"HEAD\"),      NGX_HTTP_HEAD },\n    { ngx_string(\"OPTIONS\"),   NGX_HTTP_OPTIONS },\n    { ngx_string(\"PROPFIND\"),  NGX_HTTP_PROPFIND },\n    { ngx_string(\"PUT\"),       NGX_HTTP_PUT },\n    { ngx_string(\"MKCOL\"),     NGX_HTTP_MKCOL },\n    { ngx_string(\"DELETE\"),    NGX_HTTP_DELETE },\n    { ngx_string(\"COPY\"),      NGX_HTTP_COPY },\n    { ngx_string(\"MOVE\"),      NGX_HTTP_MOVE },\n    { ngx_string(\"PROPPATCH\"), NGX_HTTP_PROPPATCH },\n    { ngx_string(\"LOCK\"),      NGX_HTTP_LOCK },\n    { ngx_string(\"UNLOCK\"),    NGX_HTTP_UNLOCK },\n    { ngx_string(\"PATCH\"),     NGX_HTTP_PATCH },\n    { ngx_string(\"TRACE\"),     NGX_HTTP_TRACE },\n    { ngx_string(\"CONNECT\"),   NGX_HTTP_CONNECT }\n};\n\n\nvoid\nngx_http_v3_init(ngx_connection_t *c)\n{\n    uint64_t                   n;\n    ngx_event_t               *rev;\n    ngx_http_connection_t     *hc;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (ngx_http_v3_init_session(c) != NGX_OK) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (c->quic->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {\n        ngx_http_v3_init_uni_stream(c);\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 init request stream\");\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, 1);\n#endif\n\n    hc = c->data;\n\n    clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);\n\n    n = c->quic->id >> 2;\n\n    if (n >= clcf->keepalive_requests * 2) {\n        ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD,\n                                        \"too many requests per connection\");\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    h3c = ngx_http_v3_get_session(c);\n\n    if (h3c->goaway) {\n        c->close = 1;\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (n + 1 == clcf->keepalive_requests\n        || ngx_current_msec - c->quic->parent->start_time\n           > clcf->keepalive_time)\n    {\n        h3c->goaway = 1;\n\n        if (ngx_http_v3_send_goaway(c, (n + 1) << 2) != NGX_OK) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        ngx_http_v3_shutdown_connection(c, NGX_HTTP_V3_ERR_NO_ERROR,\n                                        \"reached maximum number of requests\");\n    }\n\n    rev = c->read;\n    rev->handler = ngx_http_v3_wait_request_handler;\n    c->write->handler = ngx_http_empty_handler;\n\n    if (rev->ready) {\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_v3_wait_request_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    ngx_pool_cleanup_t        *cln;\n    ngx_http_request_t        *r;\n    ngx_http_connection_t     *hc;\n    ngx_http_v3_session_t     *h3c;\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, \"http3 wait request 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_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_pfree(c->pool, b->start) == NGX_OK) {\n            b->start = NULL;\n        }\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\n    c->log->action = \"reading client request\";\n\n    ngx_reusable_connection(c, 0);\n\n    r = ngx_http_create_request(c);\n    if (r == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    r->http_version = NGX_HTTP_VERSION_30;\n\n    r->v3_parse = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_parse_t));\n    if (r->v3_parse == NULL) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    r->v3_parse->header_limit = cscf->large_client_header_buffers.size\n                                * cscf->large_client_header_buffers.num;\n\n    c->data = r;\n    c->requests = (c->quic->id >> 2) + 1;\n\n    cln = ngx_pool_cleanup_add(r->pool, 0);\n    if (cln == NULL) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    cln->handler = ngx_http_v3_cleanup_request;\n    cln->data = r;\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->nrequests++;\n\n    if (h3c->keepalive.timer_set) {\n        ngx_del_timer(&h3c->keepalive);\n    }\n\n    rev->handler = ngx_http_v3_process_request;\n    ngx_http_v3_process_request(rev);\n}\n\n\nvoid\nngx_http_v3_reset_connection(ngx_connection_t *c)\n{\n    ngx_http_v3_srv_conf_t  *h3scf;\n\n    h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);\n\n    if (h3scf->max_table_capacity > 0 && !c->read->eof) {\n        (void) ngx_http_v3_send_cancel_stream(c, c->quic->id);\n    }\n\n    if (c->timedout) {\n        ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR);\n\n    } else if (c->close) {\n        ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_REQUEST_REJECTED);\n\n    } else if (c->requests == 0 || c->error) {\n        ngx_quic_reset_stream(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR);\n    }\n}\n\n\nstatic void\nngx_http_v3_cleanup_request(void *data)\n{\n    ngx_http_request_t  *r = data;\n\n    ngx_connection_t          *c;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n\n    if (!r->response_sent) {\n        c->error = 1;\n    }\n\n    h3c = ngx_http_v3_get_session(c);\n\n    if (--h3c->nrequests == 0) {\n        clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module);\n        ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout);\n    }\n}\n\n\nstatic void\nngx_http_v3_process_request(ngx_event_t *rev)\n{\n    u_char                       *p;\n    ssize_t                       n;\n    ngx_buf_t                    *b;\n    ngx_int_t                     rc;\n    ngx_connection_t             *c;\n    ngx_http_request_t           *r;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_core_srv_conf_t     *cscf;\n    ngx_http_v3_parse_headers_t  *st;\n\n    c = rev->data;\n    r = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, \"http3 process request\");\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    h3c = ngx_http_v3_get_session(c);\n\n    st = &r->v3_parse->headers;\n\n    b = r->header_in;\n\n    for ( ;; ) {\n\n        if (b->pos == b->last) {\n\n            if (rev->ready) {\n                n = c->recv(c, b->start, b->end - b->start);\n\n            } else {\n                n = NGX_AGAIN;\n            }\n\n            if (n == NGX_AGAIN) {\n                if (!rev->timer_set) {\n                    cscf = ngx_http_get_module_srv_conf(r,\n                                                        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                }\n\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                c->log->action = \"reading client request\";\n\n                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n                break;\n            }\n\n            b->pos = b->start;\n            b->last = b->start + n;\n        }\n\n        p = b->pos;\n\n        rc = ngx_http_v3_parse_headers(c, st, b);\n\n        if (rc > 0) {\n            ngx_quic_reset_stream(c, rc);\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"client sent invalid header\");\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        r->request_length += b->pos - p;\n        h3c->total_bytes += b->pos - p;\n\n        if (ngx_http_v3_check_flood(c) != NGX_OK) {\n            ngx_http_close_request(r, NGX_HTTP_CLOSE);\n            break;\n        }\n\n        if (rc == NGX_BUSY) {\n            if (rev->error) {\n                ngx_http_close_request(r, NGX_HTTP_CLOSE);\n                break;\n            }\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            }\n\n            break;\n        }\n\n        if (rc == NGX_AGAIN) {\n            continue;\n        }\n\n        /* rc == NGX_OK || rc == NGX_DONE */\n\n        h3c->payload_bytes += ngx_http_v3_encode_field_l(NULL,\n                                                   &st->field_rep.field.name,\n                                                   &st->field_rep.field.value);\n\n        if (ngx_http_v3_process_header(r, &st->field_rep.field.name,\n                                       &st->field_rep.field.value)\n            != NGX_OK)\n        {\n            break;\n        }\n\n        if (rc == NGX_DONE) {\n            if (ngx_http_v3_process_request_header(r) != NGX_OK) {\n                break;\n            }\n\n            ngx_http_process_request(r);\n            break;\n        }\n    }\n\n    ngx_http_run_posted_requests(c);\n\n    return;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_process_header(ngx_http_request_t *r, ngx_str_t *name,\n    ngx_str_t *value)\n{\n    size_t                      len;\n    ngx_table_elt_t            *h;\n    ngx_http_header_t          *hh;\n    ngx_http_core_srv_conf_t   *cscf;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    len = name->len + value->len;\n\n    if (len > r->v3_parse->header_limit) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent too large header\");\n        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE);\n        return NGX_ERROR;\n    }\n\n    r->v3_parse->header_limit -= len;\n\n    if (ngx_http_v3_validate_header(r, name, value) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        return NGX_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\\\"\", name);\n\n            return NGX_OK;\n        }\n    }\n\n    if (name->len &&  name->data[0] == ':') {\n        return ngx_http_v3_process_pseudo_header(r, name, value);\n    }\n\n    if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\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        return NGX_ERROR;\n    }\n\n    h->key = *name;\n    h->value = *value;\n    h->lowcase_key = h->key.data;\n    h->hash = ngx_hash_key(h->key.data, h->key.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, 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    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http3 header: \\\"%V: %V\\\"\", name, value);\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_validate_header(ngx_http_request_t *r, ngx_str_t *name,\n    ngx_str_t *value)\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 = (name->data[0] == ':'); i != name->len; i++) {\n        ch = 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\\\"\", name);\n\n            return NGX_ERROR;\n        }\n\n        r->invalid_header = 1;\n    }\n\n    for (i = 0; i != value->len; i++) {\n        ch = 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\\\"\", name, value);\n\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_process_pseudo_header(ngx_http_request_t *r, ngx_str_t *name,\n    ngx_str_t *value)\n{\n    u_char      ch, c;\n    ngx_uint_t  i;\n\n    if (r->request_line.len) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent out of order pseudo-headers\");\n        goto failed;\n    }\n\n    if (name->len == 7 && ngx_strncmp(name->data, \":method\", 7) == 0) {\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            goto failed;\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            goto failed;\n        }\n\n        r->method_name = *value;\n\n        for (i = 0; i < sizeof(ngx_http_v3_methods)\n                        / sizeof(ngx_http_v3_methods[0]); i++)\n        {\n            if (value->len == ngx_http_v3_methods[i].name.len\n                && ngx_strncmp(value->data,\n                               ngx_http_v3_methods[i].name.data, value->len)\n                   == 0)\n            {\n                r->method = ngx_http_v3_methods[i].method;\n                break;\n            }\n        }\n\n        for (i = 0; i < value->len; i++) {\n            ch = value->data[i];\n\n            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {\n                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                              \"client sent invalid method: \\\"%V\\\"\", value);\n                goto failed;\n            }\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 method \\\"%V\\\" %ui\", value, r->method);\n        return NGX_OK;\n    }\n\n    if (name->len == 5 && ngx_strncmp(name->data, \":path\", 5) == 0) {\n\n        if (r->uri_start) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent duplicate \\\":path\\\" header\");\n            goto failed;\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            goto failed;\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\\\"\",\n                          value);\n            goto failed;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 path \\\"%V\\\"\", value);\n        return NGX_OK;\n    }\n\n    if (name->len == 7 && ngx_strncmp(name->data, \":scheme\", 7) == 0) {\n\n        if (r->schema.len) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent duplicate \\\":scheme\\\" header\");\n            goto failed;\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            goto failed;\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')\n                 || 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\\\"\",\n                          value);\n            goto failed;\n        }\n\n        r->schema = *value;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 schema \\\"%V\\\"\", value);\n        return NGX_OK;\n    }\n\n    if (name->len == 10 && ngx_strncmp(name->data, \":authority\", 10) == 0) {\n\n        if (r->host_start) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent duplicate \\\":authority\\\" header\");\n            goto failed;\n        }\n\n        r->host_start = value->data;\n        r->host_end = value->data + value->len;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 authority \\\"%V\\\"\", value);\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                  \"client sent unknown pseudo-header \\\"%V\\\"\", name);\n\nfailed:\n\n    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_init_pseudo_headers(ngx_http_request_t *r)\n{\n    size_t      len;\n    u_char     *p;\n    ngx_int_t   rc;\n    ngx_str_t   host;\n\n    if (r->request_line.len) {\n        return NGX_OK;\n    }\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        goto failed;\n    }\n\n    if (r->schema.len == 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent no \\\":scheme\\\" header\");\n        goto failed;\n    }\n\n    if (r->uri_start == NULL) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent no \\\":path\\\" header\");\n        goto failed;\n    }\n\n    len = r->method_name.len + 1\n          + (r->uri_end - r->uri_start) + 1\n          + sizeof(\"HTTP/3.0\") - 1;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        ngx_http_close_request(r, 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    *p++ = ' ';\n    p = ngx_cpymem(p, r->uri_start, r->uri_end - r->uri_start);\n    *p++ = ' ';\n    p = ngx_cpymem(p, \"HTTP/3.0\", sizeof(\"HTTP/3.0\") - 1);\n\n    r->request_line.len = p - r->request_line.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http3 request line: \\\"%V\\\"\", &r->request_line);\n\n    ngx_str_set(&r->http_protocol, \"HTTP/3.0\");\n\n    if (ngx_http_process_request_uri(r) != NGX_OK) {\n        return NGX_ERROR;\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, r->connection->log, 0,\n                          \"client sent invalid host in request line\");\n            goto failed;\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 (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        r->headers_in.server = host;\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        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_process_request_header(ngx_http_request_t *r)\n{\n    ssize_t            n;\n    ngx_buf_t         *b;\n    ngx_connection_t  *c;\n\n    c = r->connection;\n\n    if (ngx_http_v3_init_pseudo_headers(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (r->headers_in.server.len == 0) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client sent neither \\\":authority\\\" nor \\\"Host\\\" header\");\n        goto failed;\n    }\n\n    if (r->headers_in.host) {\n        if (r->headers_in.host->value.len != r->headers_in.server.len\n            || ngx_memcmp(r->headers_in.host->value.data,\n                          r->headers_in.server.data,\n                          r->headers_in.server.len)\n               != 0)\n        {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client sent \\\":authority\\\" and \\\"Host\\\" headers \"\n                          \"with different values\");\n            goto failed;\n        }\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, c->log, 0,\n                          \"client sent invalid \\\"Content-Length\\\" header\");\n            goto failed;\n        }\n\n    } else {\n        b = r->header_in;\n        n = b->last - b->pos;\n\n        if (n == 0) {\n            n = c->recv(c, b->start, b->end - b->start);\n\n            if (n == NGX_ERROR) {\n                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return NGX_ERROR;\n            }\n\n            if (n > 0) {\n                b->pos = b->start;\n                b->last = b->start + n;\n            }\n        }\n\n        if (n != 0) {\n            r->headers_in.chunked = 1;\n        }\n    }\n\n    if (r->method == NGX_HTTP_CONNECT) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"client sent CONNECT method\");\n        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);\n        return NGX_ERROR;\n    }\n\n    if (r->method == NGX_HTTP_TRACE) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"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\nfailed:\n\n    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_read_request_body(ngx_http_request_t *r)\n{\n    size_t                     preread;\n    ngx_int_t                  rc;\n    ngx_chain_t               *cl, out;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    rb = r->request_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                       \"http3 client request body preread %uz\", preread);\n\n        out.buf = r->header_in;\n        out.next = NULL;\n        cl = &out;\n\n    } else {\n        cl = NULL;\n    }\n\n    rc = ngx_http_v3_request_body_filter(r, cl);\n    if (rc != NGX_OK) {\n        return rc;\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        rb->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        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    rb->buf = ngx_create_temp_buf(r->pool, clcf->client_body_buffer_size);\n    if (rb->buf == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\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_http_v3_do_read_client_request_body(r);\n}\n\n\nstatic void\nngx_http_v3_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_v3_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\nngx_int_t\nngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r)\n{\n    ngx_int_t  rc;\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_v3_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 ngx_int_t\nngx_http_v3_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                   \"http3 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_v3_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                           \"http3 client request body recv %z\", n);\n\n            if (n == NGX_AGAIN) {\n                break;\n            }\n\n            if (n == 0) {\n                rb->buf->last_buf = 1;\n            }\n\n            if (n == NGX_ERROR) {\n                c->error = 1;\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n            rb->buf->last += n;\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_v3_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                       \"http3 client request body rest %O\", rb->rest);\n\n        if (flush) {\n            rc = ngx_http_v3_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 (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_v3_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    off_t                      max;\n    size_t                     size;\n    u_char                    *p;\n    ngx_int_t                  rc;\n    ngx_buf_t                 *b;\n    ngx_uint_t                 last;\n    ngx_chain_t               *cl, *out, *tl, **ll;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n    ngx_http_v3_parse_data_t  *st;\n\n    rb = r->request_body;\n    st = &r->v3_parse->body;\n\n    h3c = ngx_http_v3_get_session(r->connection);\n\n    if (rb->rest == -1) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 request body filter\");\n\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        rb->rest = cscf->large_client_header_buffers.size;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    max = r->headers_in.content_length_n;\n\n    if (max == -1 && clcf->client_max_body_size) {\n        max = clcf->client_max_body_size;\n    }\n\n    out = NULL;\n    ll = &out;\n    last = 0;\n\n    for (cl = in; cl; cl = cl->next) {\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                       \"http3 body 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        if (cl->buf->last_buf) {\n            last = 1;\n        }\n\n        b = NULL;\n\n        while (cl->buf->pos < cl->buf->last) {\n\n            if (st->length == 0) {\n                p = cl->buf->pos;\n\n                rc = ngx_http_v3_parse_data(r->connection, st, cl->buf);\n\n                r->request_length += cl->buf->pos - p;\n                h3c->total_bytes += cl->buf->pos - p;\n\n                if (ngx_http_v3_check_flood(r->connection) != NGX_OK) {\n                    return NGX_HTTP_CLOSE;\n                }\n\n                if (rc == NGX_AGAIN) {\n                    continue;\n                }\n\n                if (rc == NGX_DONE) {\n                    last = 1;\n                    goto done;\n                }\n\n                if (rc > 0) {\n                    ngx_quic_reset_stream(r->connection, rc);\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"client sent invalid body\");\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n\n                if (rc == NGX_ERROR) {\n                    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                }\n\n                /* rc == NGX_OK */\n            }\n\n            if (max != -1 && (uint64_t) (max - rb->received) < st->length) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"client intended to send too large \"\n                              \"body: %O+%ui bytes\",\n                              rb->received, st->length);\n\n                return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;\n            }\n\n            if (b\n                && st->length <= 128\n                && (uint64_t) (cl->buf->last - cl->buf->pos) >= st->length)\n            {\n                rb->received += st->length;\n                r->request_length += st->length;\n                h3c->total_bytes += st->length;\n                h3c->payload_bytes += st->length;\n\n                if (st->length < 8) {\n\n                    while (st->length) {\n                        *b->last++ = *cl->buf->pos++;\n                        st->length--;\n                    }\n\n                } else {\n                    ngx_memmove(b->last, cl->buf->pos, st->length);\n                    b->last += st->length;\n                    cl->buf->pos += st->length;\n                    st->length = 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 (size > st->length) {\n                cl->buf->pos += (size_t) st->length;\n                rb->received += st->length;\n                r->request_length += st->length;\n                h3c->total_bytes += st->length;\n                h3c->payload_bytes += st->length;\n                st->length = 0;\n\n            } else {\n                st->length -= size;\n                rb->received += size;\n                r->request_length += size;\n                h3c->total_bytes += size;\n                h3c->payload_bytes += size;\n                cl->buf->pos = cl->buf->last;\n            }\n\n            b->last = cl->buf->pos;\n        }\n    }\n\ndone:\n\n    if (last) {\n\n        if (st->length > 0) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client prematurely closed stream\");\n            r->connection->error = 1;\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        if (r->headers_in.content_length_n == -1) {\n            r->headers_in.content_length_n = rb->received;\n\n        } else if (r->headers_in.content_length_n != rb->received) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent less body data than expected: \"\n                          \"%O out of %O bytes of request body received\",\n                          rb->received, r->headers_in.content_length_n);\n            return NGX_HTTP_BAD_REQUEST;\n        }\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\n    } else {\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 = (off_t) cscf->large_client_header_buffers.size;\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"
  },
  {
    "path": "src/http/v3/ngx_http_v3_streams.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_http_v3_parse_uni_t         parse;\n    ngx_int_t                       index;\n} ngx_http_v3_uni_stream_t;\n\n\ntypedef struct {\n    ngx_queue_t                     queue;\n    uint64_t                        id;\n    ngx_connection_t               *connection;\n    ngx_uint_t                     *npushing;\n} ngx_http_v3_push_t;\n\n\nstatic void ngx_http_v3_close_uni_stream(ngx_connection_t *c);\nstatic void ngx_http_v3_uni_read_handler(ngx_event_t *rev);\nstatic void ngx_http_v3_dummy_write_handler(ngx_event_t *wev);\nstatic void ngx_http_v3_push_cleanup(void *data);\nstatic ngx_connection_t *ngx_http_v3_get_uni_stream(ngx_connection_t *c,\n    ngx_uint_t type);\n\n\nvoid\nngx_http_v3_init_uni_stream(ngx_connection_t *c)\n{\n    uint64_t                   n;\n    ngx_http_v3_srv_conf_t    *h3scf;\n    ngx_http_v3_uni_stream_t  *us;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 init uni stream\");\n\n    h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);\n\n    n = c->quic->id >> 2;\n\n    if (n >= h3scf->max_uni_streams) {\n        ngx_http_v3_finalize_connection(c,\n                                      NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR,\n                                      \"reached maximum number of uni streams\");\n        c->data = NULL;\n        ngx_http_v3_close_uni_stream(c);\n        return;\n    }\n\n    c->quic->cancelable = 1;\n\n    us = ngx_pcalloc(c->pool, sizeof(ngx_http_v3_uni_stream_t));\n    if (us == NULL) {\n        ngx_http_v3_finalize_connection(c,\n                                        NGX_HTTP_V3_ERR_INTERNAL_ERROR,\n                                        \"memory allocation error\");\n        c->data = NULL;\n        ngx_http_v3_close_uni_stream(c);\n        return;\n    }\n\n    us->index = -1;\n\n    c->data = us;\n\n    c->read->handler = ngx_http_v3_uni_read_handler;\n    c->write->handler = ngx_http_v3_dummy_write_handler;\n\n    ngx_http_v3_uni_read_handler(c->read);\n}\n\n\nstatic void\nngx_http_v3_close_uni_stream(ngx_connection_t *c)\n{\n    ngx_pool_t                *pool;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_v3_uni_stream_t  *us;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 close stream\");\n\n    us = c->data;\n\n    if (us && us->index >= 0) {\n        h3c = ngx_http_v3_get_session(c);\n        h3c->known_streams[us->index] = NULL;\n    }\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\nngx_int_t\nngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type)\n{\n    ngx_int_t                  index;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_v3_uni_stream_t  *us;\n\n    h3c = ngx_http_v3_get_session(c);\n\n    switch (type) {\n\n    case NGX_HTTP_V3_STREAM_ENCODER:\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 encoder stream\");\n        index = NGX_HTTP_V3_STREAM_CLIENT_ENCODER;\n        break;\n\n    case NGX_HTTP_V3_STREAM_DECODER:\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 decoder stream\");\n        index = NGX_HTTP_V3_STREAM_CLIENT_DECODER;\n        break;\n\n    case NGX_HTTP_V3_STREAM_CONTROL:\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 control stream\");\n        index = NGX_HTTP_V3_STREAM_CLIENT_CONTROL;\n\n        break;\n\n    default:\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 stream 0x%02xL\", type);\n\n        if (h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_ENCODER] == NULL\n            || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_DECODER] == NULL\n            || h3c->known_streams[NGX_HTTP_V3_STREAM_CLIENT_CONTROL] == NULL)\n        {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0, \"missing mandatory stream\");\n            return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR;\n        }\n\n        index = -1;\n    }\n\n    if (index >= 0) {\n        if (h3c->known_streams[index]) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0, \"stream exists\");\n            return NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR;\n        }\n\n        h3c->known_streams[index] = c;\n\n        us = c->data;\n        us->index = index;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_v3_uni_read_handler(ngx_event_t *rev)\n{\n    u_char                     buf[128];\n    ssize_t                    n;\n    ngx_buf_t                  b;\n    ngx_int_t                  rc;\n    ngx_connection_t          *c;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_v3_uni_stream_t  *us;\n\n    c = rev->data;\n    us = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 read handler\");\n\n    ngx_memzero(&b, sizeof(ngx_buf_t));\n\n    while (rev->ready) {\n\n        n = c->recv(c, buf, sizeof(buf));\n\n        if (n == NGX_ERROR) {\n            rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR;\n            goto failed;\n        }\n\n        if (n == 0) {\n            if (us->index >= 0) {\n                rc = NGX_HTTP_V3_ERR_CLOSED_CRITICAL_STREAM;\n                goto failed;\n            }\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 read eof\");\n            ngx_http_v3_close_uni_stream(c);\n            return;\n        }\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        b.pos = buf;\n        b.last = buf + n;\n\n        h3c = ngx_http_v3_get_session(c);\n        h3c->total_bytes += n;\n\n        if (ngx_http_v3_check_flood(c) != NGX_OK) {\n            ngx_http_v3_close_uni_stream(c);\n            return;\n        }\n\n        rc = ngx_http_v3_parse_uni(c, &us->parse, &b);\n\n        if (rc == NGX_DONE) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http3 read done\");\n            ngx_http_v3_close_uni_stream(c);\n            return;\n        }\n\n        if (rc > 0) {\n            goto failed;\n        }\n\n        if (rc != NGX_AGAIN) {\n            rc = NGX_HTTP_V3_ERR_GENERAL_PROTOCOL_ERROR;\n            goto failed;\n        }\n    }\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        rc = NGX_HTTP_V3_ERR_INTERNAL_ERROR;\n        goto failed;\n    }\n\n    return;\n\nfailed:\n\n    ngx_http_v3_finalize_connection(c, rc, \"stream error\");\n    ngx_http_v3_close_uni_stream(c);\n}\n\n\nstatic void\nngx_http_v3_dummy_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_HTTP, c->log, 0, \"http3 dummy write handler\");\n\n    if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n        ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_INTERNAL_ERROR,\n                                        NULL);\n        ngx_http_v3_close_uni_stream(c);\n    }\n}\n\n\n/* XXX async & buffered stream writes */\n\nngx_connection_t *\nngx_http_v3_create_push_stream(ngx_connection_t *c, uint64_t push_id)\n{\n    u_char                 *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 2];\n    size_t                  n;\n    ngx_connection_t       *sc;\n    ngx_pool_cleanup_t     *cln;\n    ngx_http_v3_push_t     *push;\n    ngx_http_v3_session_t  *h3c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 create push stream id:%uL\", push_id);\n\n    sc = ngx_quic_open_stream(c, 0);\n    if (sc == NULL) {\n        goto failed;\n    }\n\n    p = buf;\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p, NGX_HTTP_V3_STREAM_PUSH);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p, push_id);\n    n = p - buf;\n\n    ngx_quic_add_exemptions(sc, n);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->total_bytes += n;\n\n    if (sc->send(sc, buf, n) != (ssize_t) n) {\n        goto failed;\n    }\n\n    cln = ngx_pool_cleanup_add(sc->pool, sizeof(ngx_http_v3_push_t));\n    if (cln == NULL) {\n        goto failed;\n    }\n\n    h3c->npushing++;\n\n    cln->handler = ngx_http_v3_push_cleanup;\n\n    push = cln->data;\n    push->id = push_id;\n    push->connection = sc;\n    push->npushing = &h3c->npushing;\n\n    ngx_queue_insert_tail(&h3c->pushing, &push->queue);\n\n    return sc;\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create push stream\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR,\n                                    \"failed to create push stream\");\n    if (sc) {\n        ngx_http_v3_close_uni_stream(sc);\n    }\n\n    return NULL;\n}\n\n\nstatic void\nngx_http_v3_push_cleanup(void *data)\n{\n    ngx_http_v3_push_t  *push = data;\n\n    ngx_queue_remove(&push->queue);\n    (*push->npushing)--;\n}\n\n\nstatic ngx_connection_t *\nngx_http_v3_get_uni_stream(ngx_connection_t *c, ngx_uint_t type)\n{\n    u_char                     buf[NGX_HTTP_V3_VARLEN_INT_LEN];\n    size_t                     n;\n    ngx_int_t                  index;\n    ngx_connection_t          *sc;\n    ngx_http_v3_session_t     *h3c;\n    ngx_http_v3_uni_stream_t  *us;\n\n    switch (type) {\n    case NGX_HTTP_V3_STREAM_ENCODER:\n        index = NGX_HTTP_V3_STREAM_SERVER_ENCODER;\n        break;\n    case NGX_HTTP_V3_STREAM_DECODER:\n        index = NGX_HTTP_V3_STREAM_SERVER_DECODER;\n        break;\n    case NGX_HTTP_V3_STREAM_CONTROL:\n        index = NGX_HTTP_V3_STREAM_SERVER_CONTROL;\n        break;\n    default:\n        index = -1;\n    }\n\n    h3c = ngx_http_v3_get_session(c);\n\n    if (index >= 0) {\n        if (h3c->known_streams[index]) {\n            return h3c->known_streams[index];\n        }\n    }\n\n    sc = ngx_quic_open_stream(c, 0);\n    if (sc == NULL) {\n        goto failed;\n    }\n\n    sc->quic->cancelable = 1;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 create uni stream, type:%ui\", type);\n\n    us = ngx_pcalloc(sc->pool, sizeof(ngx_http_v3_uni_stream_t));\n    if (us == NULL) {\n        goto failed;\n    }\n\n    us->index = index;\n\n    sc->data = us;\n\n    sc->read->handler = ngx_http_v3_uni_read_handler;\n    sc->write->handler = ngx_http_v3_dummy_write_handler;\n\n    if (index >= 0) {\n        h3c->known_streams[index] = sc;\n    }\n\n    n = (u_char *) ngx_http_v3_encode_varlen_int(buf, type) - buf;\n\n    ngx_quic_add_exemptions(sc, n);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->total_bytes += n;\n\n    if (sc->send(sc, buf, n) != (ssize_t) n) {\n        goto failed;\n    }\n\n    return sc;\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to create server stream\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_STREAM_CREATION_ERROR,\n                                    \"failed to create server stream\");\n    if (sc) {\n        ngx_http_v3_close_uni_stream(sc);\n    }\n\n    return NULL;\n}\n\n\nngx_int_t\nngx_http_v3_send_settings(ngx_connection_t *c)\n{\n    u_char                  *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 6];\n    size_t                   n;\n    ngx_connection_t        *cc;\n    ngx_http_v3_session_t   *h3c;\n    ngx_http_v3_srv_conf_t  *h3scf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 send settings\");\n\n    cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL);\n    if (cc == NULL) {\n        return NGX_ERROR;\n    }\n\n    h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);\n\n    n = ngx_http_v3_encode_varlen_int(NULL,\n                                      NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY);\n    n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_table_capacity);\n    n += ngx_http_v3_encode_varlen_int(NULL, NGX_HTTP_V3_PARAM_BLOCKED_STREAMS);\n    n += ngx_http_v3_encode_varlen_int(NULL, h3scf->max_blocked_streams);\n\n    p = (u_char *) ngx_http_v3_encode_varlen_int(buf,\n                                                 NGX_HTTP_V3_FRAME_SETTINGS);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p, n);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p,\n                                         NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_table_capacity);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p,\n                                            NGX_HTTP_V3_PARAM_BLOCKED_STREAMS);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p, h3scf->max_blocked_streams);\n    n = p - buf;\n\n    ngx_quic_add_exemptions(cc, n);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->total_bytes += n;\n\n    if (cc->send(cc, buf, n) != (ssize_t) n) {\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to send settings\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD,\n                                    \"failed to send settings\");\n    ngx_http_v3_close_uni_stream(cc);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id)\n{\n    u_char                 *p, buf[NGX_HTTP_V3_VARLEN_INT_LEN * 3];\n    size_t                  n;\n    ngx_connection_t       *cc;\n    ngx_http_v3_session_t  *h3c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 send goaway %uL\", id);\n\n    cc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_CONTROL);\n    if (cc == NULL) {\n        return NGX_ERROR;\n    }\n\n    n = ngx_http_v3_encode_varlen_int(NULL, id);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(buf, NGX_HTTP_V3_FRAME_GOAWAY);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p, n);\n    p = (u_char *) ngx_http_v3_encode_varlen_int(p, id);\n    n = p - buf;\n\n    ngx_quic_add_exemptions(cc, n);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->total_bytes += n;\n\n    if (cc->send(cc, buf, n) != (ssize_t) n) {\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to send goaway\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD,\n                                    \"failed to send goaway\");\n    ngx_http_v3_close_uni_stream(cc);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_send_ack_section(ngx_connection_t *c, ngx_uint_t stream_id)\n{\n    u_char                  buf[NGX_HTTP_V3_PREFIX_INT_LEN];\n    size_t                  n;\n    ngx_connection_t       *dc;\n    ngx_http_v3_session_t  *h3c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 send section acknowledgement %ui\", stream_id);\n\n    dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER);\n    if (dc == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf[0] = 0x80;\n    n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 7) - buf;\n\n    ngx_quic_add_exemptions(dc, n);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->total_bytes += n;\n\n    if (dc->send(dc, buf, n) != (ssize_t) n) {\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                  \"failed to send section acknowledgement\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD,\n                                    \"failed to send section acknowledgement\");\n    ngx_http_v3_close_uni_stream(dc);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_send_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id)\n{\n    u_char                  buf[NGX_HTTP_V3_PREFIX_INT_LEN];\n    size_t                  n;\n    ngx_connection_t       *dc;\n    ngx_http_v3_session_t  *h3c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 send stream cancellation %ui\", stream_id);\n\n    dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER);\n    if (dc == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf[0] = 0x40;\n    n = (u_char *) ngx_http_v3_encode_prefix_int(buf, stream_id, 6) - buf;\n\n    ngx_quic_add_exemptions(dc, n);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->total_bytes += n;\n\n    if (dc->send(dc, buf, n) != (ssize_t) n) {\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0, \"failed to send stream cancellation\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD,\n                                    \"failed to send stream cancellation\");\n    ngx_http_v3_close_uni_stream(dc);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_send_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc)\n{\n    u_char                  buf[NGX_HTTP_V3_PREFIX_INT_LEN];\n    size_t                  n;\n    ngx_connection_t       *dc;\n    ngx_http_v3_session_t  *h3c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 send insert count increment %ui\", inc);\n\n    dc = ngx_http_v3_get_uni_stream(c, NGX_HTTP_V3_STREAM_DECODER);\n    if (dc == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf[0] = 0;\n    n = (u_char *) ngx_http_v3_encode_prefix_int(buf, inc, 6) - buf;\n\n    ngx_quic_add_exemptions(dc, n);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3c->total_bytes += n;\n\n    if (dc->send(dc, buf, n) != (ssize_t) n) {\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                  \"failed to send insert count increment\");\n\n    ngx_http_v3_finalize_connection(c, NGX_HTTP_V3_ERR_EXCESSIVE_LOAD,\n                                    \"failed to send insert count increment\");\n    ngx_http_v3_close_uni_stream(dc);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_set_max_push_id(ngx_connection_t *c, uint64_t max_push_id)\n{\n    ngx_http_v3_session_t  *h3c;\n\n    h3c = ngx_http_v3_get_session(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 MAX_PUSH_ID:%uL\", max_push_id);\n\n    if (h3c->max_push_id != (uint64_t) -1 && max_push_id < h3c->max_push_id) {\n        return NGX_HTTP_V3_ERR_ID_ERROR;\n    }\n\n    h3c->max_push_id = max_push_id;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id)\n{\n    ngx_http_v3_session_t  *h3c;\n\n    h3c = ngx_http_v3_get_session(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 GOAWAY:%uL\", push_id);\n\n    h3c->goaway_push_id = push_id;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id)\n{\n    ngx_queue_t            *q;\n    ngx_http_request_t     *r;\n    ngx_http_v3_push_t     *push;\n    ngx_http_v3_session_t  *h3c;\n\n    h3c = ngx_http_v3_get_session(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 CANCEL_PUSH:%uL\", push_id);\n\n    if (push_id >= h3c->next_push_id) {\n        return NGX_HTTP_V3_ERR_ID_ERROR;\n    }\n\n    for (q = ngx_queue_head(&h3c->pushing);\n         q != ngx_queue_sentinel(&h3c->pushing);\n         q = ngx_queue_next(&h3c->pushing))\n    {\n        push = (ngx_http_v3_push_t *) q;\n\n        if (push->id != push_id) {\n            continue;\n        }\n\n        r = push->connection->data;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http3 cancel push\");\n\n        ngx_http_finalize_request(r, NGX_HTTP_CLOSE);\n\n        break;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 cancel stream %ui\", stream_id);\n\n    /* we do not use dynamic tables */\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_streams.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_V3_STREAMS_H_INCLUDED_\n#define _NGX_HTTP_V3_STREAMS_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nvoid ngx_http_v3_init_uni_stream(ngx_connection_t *c);\nngx_int_t ngx_http_v3_register_uni_stream(ngx_connection_t *c, uint64_t type);\n\nngx_connection_t *ngx_http_v3_create_push_stream(ngx_connection_t *c,\n    uint64_t push_id);\nngx_int_t ngx_http_v3_set_max_push_id(ngx_connection_t *c,\n    uint64_t max_push_id);\nngx_int_t ngx_http_v3_goaway(ngx_connection_t *c, uint64_t push_id);\nngx_int_t ngx_http_v3_cancel_push(ngx_connection_t *c, uint64_t push_id);\nngx_int_t ngx_http_v3_cancel_stream(ngx_connection_t *c, ngx_uint_t stream_id);\n\nngx_int_t ngx_http_v3_send_settings(ngx_connection_t *c);\nngx_int_t ngx_http_v3_send_goaway(ngx_connection_t *c, uint64_t id);\nngx_int_t ngx_http_v3_send_ack_section(ngx_connection_t *c,\n    ngx_uint_t stream_id);\nngx_int_t ngx_http_v3_send_cancel_stream(ngx_connection_t *c,\n    ngx_uint_t stream_id);\nngx_int_t ngx_http_v3_send_inc_insert_count(ngx_connection_t *c,\n    ngx_uint_t inc);\n\n\n#endif /* _NGX_HTTP_V3_STREAMS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_tables.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\n#define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32)\n\n\nstatic ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t need);\nstatic void ngx_http_v3_unblock(void *data);\nstatic ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c);\n\n\ntypedef struct {\n    ngx_queue_t        queue;\n    ngx_connection_t  *connection;\n    ngx_uint_t        *nblocked;\n} ngx_http_v3_block_t;\n\n\nstatic ngx_http_v3_field_t  ngx_http_v3_static_table[] = {\n\n    { ngx_string(\":authority\"),            ngx_string(\"\") },\n    { ngx_string(\":path\"),                 ngx_string(\"/\") },\n    { ngx_string(\"age\"),                   ngx_string(\"0\") },\n    { ngx_string(\"content-disposition\"),   ngx_string(\"\") },\n    { ngx_string(\"content-length\"),        ngx_string(\"0\") },\n    { ngx_string(\"cookie\"),                ngx_string(\"\") },\n    { ngx_string(\"date\"),                  ngx_string(\"\") },\n    { ngx_string(\"etag\"),                  ngx_string(\"\") },\n    { ngx_string(\"if-modified-since\"),     ngx_string(\"\") },\n    { ngx_string(\"if-none-match\"),         ngx_string(\"\") },\n    { ngx_string(\"last-modified\"),         ngx_string(\"\") },\n    { ngx_string(\"link\"),                  ngx_string(\"\") },\n    { ngx_string(\"location\"),              ngx_string(\"\") },\n    { ngx_string(\"referer\"),               ngx_string(\"\") },\n    { ngx_string(\"set-cookie\"),            ngx_string(\"\") },\n    { ngx_string(\":method\"),               ngx_string(\"CONNECT\") },\n    { ngx_string(\":method\"),               ngx_string(\"DELETE\") },\n    { ngx_string(\":method\"),               ngx_string(\"GET\") },\n    { ngx_string(\":method\"),               ngx_string(\"HEAD\") },\n    { ngx_string(\":method\"),               ngx_string(\"OPTIONS\") },\n    { ngx_string(\":method\"),               ngx_string(\"POST\") },\n    { ngx_string(\":method\"),               ngx_string(\"PUT\") },\n    { ngx_string(\":scheme\"),               ngx_string(\"http\") },\n    { ngx_string(\":scheme\"),               ngx_string(\"https\") },\n    { ngx_string(\":status\"),               ngx_string(\"103\") },\n    { ngx_string(\":status\"),               ngx_string(\"200\") },\n    { ngx_string(\":status\"),               ngx_string(\"304\") },\n    { ngx_string(\":status\"),               ngx_string(\"404\") },\n    { ngx_string(\":status\"),               ngx_string(\"503\") },\n    { ngx_string(\"accept\"),                ngx_string(\"*/*\") },\n    { ngx_string(\"accept\"),\n          ngx_string(\"application/dns-message\") },\n    { ngx_string(\"accept-encoding\"),       ngx_string(\"gzip, deflate, br\") },\n    { ngx_string(\"accept-ranges\"),         ngx_string(\"bytes\") },\n    { ngx_string(\"access-control-allow-headers\"),\n                                           ngx_string(\"cache-control\") },\n    { ngx_string(\"access-control-allow-headers\"),\n                                           ngx_string(\"content-type\") },\n    { ngx_string(\"access-control-allow-origin\"),\n                                           ngx_string(\"*\") },\n    { ngx_string(\"cache-control\"),         ngx_string(\"max-age=0\") },\n    { ngx_string(\"cache-control\"),         ngx_string(\"max-age=2592000\") },\n    { ngx_string(\"cache-control\"),         ngx_string(\"max-age=604800\") },\n    { ngx_string(\"cache-control\"),         ngx_string(\"no-cache\") },\n    { ngx_string(\"cache-control\"),         ngx_string(\"no-store\") },\n    { ngx_string(\"cache-control\"),\n          ngx_string(\"public, max-age=31536000\") },\n    { ngx_string(\"content-encoding\"),      ngx_string(\"br\") },\n    { ngx_string(\"content-encoding\"),      ngx_string(\"gzip\") },\n    { ngx_string(\"content-type\"),\n          ngx_string(\"application/dns-message\") },\n    { ngx_string(\"content-type\"),\n          ngx_string(\"application/javascript\") },\n    { ngx_string(\"content-type\"),          ngx_string(\"application/json\") },\n    { ngx_string(\"content-type\"),\n          ngx_string(\"application/x-www-form-urlencoded\") },\n    { ngx_string(\"content-type\"),          ngx_string(\"image/gif\") },\n    { ngx_string(\"content-type\"),          ngx_string(\"image/jpeg\") },\n    { ngx_string(\"content-type\"),          ngx_string(\"image/png\") },\n    { ngx_string(\"content-type\"),          ngx_string(\"text/css\") },\n    { ngx_string(\"content-type\"),\n          ngx_string(\"text/html;charset=utf-8\") },\n    { ngx_string(\"content-type\"),          ngx_string(\"text/plain\") },\n    { ngx_string(\"content-type\"),\n          ngx_string(\"text/plain;charset=utf-8\") },\n    { ngx_string(\"range\"),                 ngx_string(\"bytes=0-\") },\n    { ngx_string(\"strict-transport-security\"),\n                                           ngx_string(\"max-age=31536000\") },\n    { ngx_string(\"strict-transport-security\"),\n          ngx_string(\"max-age=31536000;includesubdomains\") },\n    { ngx_string(\"strict-transport-security\"),\n          ngx_string(\"max-age=31536000;includesubdomains;preload\") },\n    { ngx_string(\"vary\"),                  ngx_string(\"accept-encoding\") },\n    { ngx_string(\"vary\"),                  ngx_string(\"origin\") },\n    { ngx_string(\"x-content-type-options\"),\n                                           ngx_string(\"nosniff\") },\n    { ngx_string(\"x-xss-protection\"),      ngx_string(\"1;mode=block\") },\n    { ngx_string(\":status\"),               ngx_string(\"100\") },\n    { ngx_string(\":status\"),               ngx_string(\"204\") },\n    { ngx_string(\":status\"),               ngx_string(\"206\") },\n    { ngx_string(\":status\"),               ngx_string(\"302\") },\n    { ngx_string(\":status\"),               ngx_string(\"400\") },\n    { ngx_string(\":status\"),               ngx_string(\"403\") },\n    { ngx_string(\":status\"),               ngx_string(\"421\") },\n    { ngx_string(\":status\"),               ngx_string(\"425\") },\n    { ngx_string(\":status\"),               ngx_string(\"500\") },\n    { ngx_string(\"accept-language\"),       ngx_string(\"\") },\n    { ngx_string(\"access-control-allow-credentials\"),\n                                           ngx_string(\"FALSE\") },\n    { ngx_string(\"access-control-allow-credentials\"),\n                                           ngx_string(\"TRUE\") },\n    { ngx_string(\"access-control-allow-headers\"),\n                                           ngx_string(\"*\") },\n    { ngx_string(\"access-control-allow-methods\"),\n                                           ngx_string(\"get\") },\n    { ngx_string(\"access-control-allow-methods\"),\n                                           ngx_string(\"get, post, options\") },\n    { ngx_string(\"access-control-allow-methods\"),\n                                           ngx_string(\"options\") },\n    { ngx_string(\"access-control-expose-headers\"),\n                                           ngx_string(\"content-length\") },\n    { ngx_string(\"access-control-request-headers\"),\n                                           ngx_string(\"content-type\") },\n    { ngx_string(\"access-control-request-method\"),\n                                           ngx_string(\"get\") },\n    { ngx_string(\"access-control-request-method\"),\n                                           ngx_string(\"post\") },\n    { ngx_string(\"alt-svc\"),               ngx_string(\"clear\") },\n    { ngx_string(\"authorization\"),         ngx_string(\"\") },\n    { ngx_string(\"content-security-policy\"),\n          ngx_string(\"script-src 'none';object-src 'none';base-uri 'none'\") },\n    { ngx_string(\"early-data\"),            ngx_string(\"1\") },\n    { ngx_string(\"expect-ct\"),             ngx_string(\"\") },\n    { ngx_string(\"forwarded\"),             ngx_string(\"\") },\n    { ngx_string(\"if-range\"),              ngx_string(\"\") },\n    { ngx_string(\"origin\"),                ngx_string(\"\") },\n    { ngx_string(\"purpose\"),               ngx_string(\"prefetch\") },\n    { ngx_string(\"server\"),                ngx_string(\"\") },\n    { ngx_string(\"timing-allow-origin\"),   ngx_string(\"*\") },\n    { ngx_string(\"upgrade-insecure-requests\"),\n                                           ngx_string(\"1\") },\n    { ngx_string(\"user-agent\"),            ngx_string(\"\") },\n    { ngx_string(\"x-forwarded-for\"),       ngx_string(\"\") },\n    { ngx_string(\"x-frame-options\"),       ngx_string(\"deny\") },\n    { ngx_string(\"x-frame-options\"),       ngx_string(\"sameorigin\") }\n};\n\n\nngx_int_t\nngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic,\n    ngx_uint_t index, ngx_str_t *value)\n{\n    ngx_str_t                     name;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    if (dynamic) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 ref insert dynamic[%ui] \\\"%V\\\"\", index, value);\n\n        h3c = ngx_http_v3_get_session(c);\n        dt = &h3c->table;\n\n        if (dt->base + dt->nelts <= index) {\n            return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n        }\n\n        index = dt->base + dt->nelts - 1 - index;\n\n        if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) {\n            return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n        }\n\n    } else {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 ref insert static[%ui] \\\"%V\\\"\", index, value);\n\n        if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) {\n            return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n        }\n    }\n\n    return ngx_http_v3_insert(c, &name, value);\n}\n\n\nngx_int_t\nngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value)\n{\n    u_char                       *p;\n    size_t                        size;\n    ngx_http_v3_field_t          *field;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    size = ngx_http_v3_table_entry_size(name, value);\n\n    if (ngx_http_v3_evict(c, size) != NGX_OK) {\n        return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n    }\n\n    h3c = ngx_http_v3_get_session(c);\n    dt = &h3c->table;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 insert [%ui] \\\"%V\\\":\\\"%V\\\", size:%uz\",\n                   dt->base + dt->nelts, name, value, size);\n\n    p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len,\n                  c->log);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    field = (ngx_http_v3_field_t *) p;\n\n    field->name.data = p + sizeof(ngx_http_v3_field_t);\n    field->name.len = name->len;\n    field->value.data = ngx_cpymem(field->name.data, name->data, name->len);\n    field->value.len = value->len;\n    ngx_memcpy(field->value.data, value->data, value->len);\n\n    dt->elts[dt->nelts++] = field;\n    dt->size += size;\n\n    /* TODO increment can be sent less often */\n\n    if (ngx_http_v3_send_inc_insert_count(c, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_v3_new_entry(c) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity)\n{\n    ngx_uint_t                     max, prev_max;\n    ngx_http_v3_field_t          **elts;\n    ngx_http_v3_session_t         *h3c;\n    ngx_http_v3_srv_conf_t        *h3scf;\n    ngx_http_v3_dynamic_table_t   *dt;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 set capacity %ui\", capacity);\n\n    h3c = ngx_http_v3_get_session(c);\n    h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);\n\n    if (capacity > h3scf->max_table_capacity) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client exceeded http3_max_table_capacity limit\");\n        return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n    }\n\n    dt = &h3c->table;\n\n    if (dt->size > capacity) {\n        if (ngx_http_v3_evict(c, dt->size - capacity) != NGX_OK) {\n            return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n        }\n    }\n\n    max = capacity / 32;\n    prev_max = dt->capacity / 32;\n\n    if (max > prev_max) {\n        elts = ngx_alloc(max * sizeof(void *), c->log);\n        if (elts == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (dt->elts) {\n            ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *));\n            ngx_free(dt->elts);\n        }\n\n        dt->elts = elts;\n    }\n\n    dt->capacity = capacity;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c)\n{\n    ngx_uint_t                    n;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    dt = &h3c->table;\n\n    if (dt->elts == NULL) {\n        return;\n    }\n\n    for (n = 0; n < dt->nelts; n++) {\n        ngx_free(dt->elts[n]);\n    }\n\n    ngx_free(dt->elts);\n}\n\n\nstatic ngx_int_t\nngx_http_v3_evict(ngx_connection_t *c, size_t need)\n{\n    size_t                        size, target;\n    ngx_uint_t                    n;\n    ngx_http_v3_field_t          *field;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    h3c = ngx_http_v3_get_session(c);\n    dt = &h3c->table;\n\n    if (need > dt->capacity) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"not enough dynamic table capacity\");\n        return NGX_ERROR;\n    }\n\n    target = dt->capacity - need;\n    n = 0;\n\n    while (dt->size > target) {\n        field = dt->elts[n++];\n        size = ngx_http_v3_table_entry_size(&field->name, &field->value);\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 evict [%ui] \\\"%V\\\":\\\"%V\\\" size:%uz\",\n                       dt->base, &field->name, &field->value, size);\n\n        ngx_free(field);\n        dt->size -= size;\n    }\n\n    if (n) {\n        dt->nelts -= n;\n        dt->base += n;\n        ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *));\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index)\n{\n    ngx_str_t                     name, value;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 duplicate %ui\", index);\n\n    h3c = ngx_http_v3_get_session(c);\n    dt = &h3c->table;\n\n    if (dt->base + dt->nelts <= index) {\n        return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n    }\n\n    index = dt->base + dt->nelts - 1 - index;\n\n    if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) {\n        return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;\n    }\n\n    return ngx_http_v3_insert(c, &name, &value);\n}\n\n\nngx_int_t\nngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 ack section %ui\", stream_id);\n\n    /* we do not use dynamic tables */\n\n    return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 increment insert count %ui\", inc);\n\n    /* we do not use dynamic tables */\n\n    return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;\n}\n\n\nngx_int_t\nngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index,\n    ngx_str_t *name, ngx_str_t *value)\n{\n    ngx_uint_t            nelts;\n    ngx_http_v3_field_t  *field;\n\n    nelts = sizeof(ngx_http_v3_static_table)\n            / sizeof(ngx_http_v3_static_table[0]);\n\n    if (index >= nelts) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 static[%ui] lookup out of bounds: %ui\",\n                       index, nelts);\n        return NGX_ERROR;\n    }\n\n    field = &ngx_http_v3_static_table[index];\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 static[%ui] lookup \\\"%V\\\":\\\"%V\\\"\",\n                   index, &field->name, &field->value);\n\n    if (name) {\n        *name = field->name;\n    }\n\n    if (value) {\n        *value = field->value;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name,\n    ngx_str_t *value)\n{\n    ngx_http_v3_field_t          *field;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    h3c = ngx_http_v3_get_session(c);\n    dt = &h3c->table;\n\n    if (index < dt->base || index - dt->base >= dt->nelts) {\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]\",\n                       index, dt->base, dt->base + dt->nelts);\n        return NGX_ERROR;\n    }\n\n    field = dt->elts[index - dt->base];\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 dynamic[%ui] lookup \\\"%V\\\":\\\"%V\\\"\",\n                   index, &field->name, &field->value);\n\n    if (name) {\n        *name = field->name;\n    }\n\n    if (value) {\n        *value = field->value;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count)\n{\n    ngx_uint_t                    max_entries, full_range, max_value,\n                                  max_wrapped, req_insert_count;\n    ngx_http_v3_srv_conf_t       *h3scf;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    /* QPACK 4.5.1.1. Required Insert Count */\n\n    if (*insert_count == 0) {\n        return NGX_OK;\n    }\n\n    h3c = ngx_http_v3_get_session(c);\n    dt = &h3c->table;\n\n    h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);\n\n    max_entries = h3scf->max_table_capacity / 32;\n    full_range = 2 * max_entries;\n\n    if (*insert_count > full_range) {\n        return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;\n    }\n\n    max_value = dt->base + dt->nelts + max_entries;\n    max_wrapped = (max_value / full_range) * full_range;\n    req_insert_count = max_wrapped + *insert_count - 1;\n\n    if (req_insert_count > max_value) {\n        if (req_insert_count <= full_range) {\n            return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;\n        }\n\n        req_insert_count -= full_range;\n    }\n\n    if (req_insert_count == 0) {\n        return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 decode insert_count %ui -> %ui\",\n                   *insert_count, req_insert_count);\n\n    *insert_count = req_insert_count;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count)\n{\n    size_t                        n;\n    ngx_pool_cleanup_t           *cln;\n    ngx_http_v3_block_t          *block;\n    ngx_http_v3_session_t        *h3c;\n    ngx_http_v3_srv_conf_t       *h3scf;\n    ngx_http_v3_dynamic_table_t  *dt;\n\n    h3c = ngx_http_v3_get_session(c);\n    dt = &h3c->table;\n\n    n = dt->base + dt->nelts;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 check insert count req:%ui, have:%ui\",\n                   insert_count, n);\n\n    if (n >= insert_count) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http3 block stream\");\n\n    block = NULL;\n\n    for (cln = c->pool->cleanup; cln; cln = cln->next) {\n        if (cln->handler == ngx_http_v3_unblock) {\n            block = cln->data;\n            break;\n        }\n    }\n\n    if (block == NULL) {\n        cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t));\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        cln->handler = ngx_http_v3_unblock;\n\n        block = cln->data;\n        block->queue.prev = NULL;\n        block->connection = c;\n        block->nblocked = &h3c->nblocked;\n    }\n\n    if (block->queue.prev == NULL) {\n        h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);\n\n        if (h3c->nblocked == h3scf->max_blocked_streams) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client exceeded http3_max_blocked_streams limit\");\n\n            ngx_http_v3_finalize_connection(c,\n                                          NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED,\n                                          \"too many blocked streams\");\n            return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;\n        }\n\n        h3c->nblocked++;\n        ngx_queue_insert_tail(&h3c->blocked, &block->queue);\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 blocked:%ui\", h3c->nblocked);\n\n    return NGX_BUSY;\n}\n\n\nstatic void\nngx_http_v3_unblock(void *data)\n{\n    ngx_http_v3_block_t  *block = data;\n\n    if (block->queue.prev) {\n        ngx_queue_remove(&block->queue);\n        block->queue.prev = NULL;\n        (*block->nblocked)--;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_v3_new_entry(ngx_connection_t *c)\n{\n    ngx_queue_t            *q;\n    ngx_connection_t       *bc;\n    ngx_http_v3_block_t    *block;\n    ngx_http_v3_session_t  *h3c;\n\n    h3c = ngx_http_v3_get_session(c);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http3 new dynamic entry, blocked:%ui\", h3c->nblocked);\n\n    while (!ngx_queue_empty(&h3c->blocked)) {\n        q = ngx_queue_head(&h3c->blocked);\n        block = (ngx_http_v3_block_t *) q;\n        bc = block->connection;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, \"http3 unblock stream\");\n\n        ngx_http_v3_unblock(block);\n        ngx_post_event(bc->read, &ngx_posted_events);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value)\n{\n    switch (id) {\n\n    case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY:\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 param QPACK_MAX_TABLE_CAPACITY:%uL\", value);\n        break;\n\n    case NGX_HTTP_V3_PARAM_MAX_HEADER_LIST_SIZE:\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 param SETTINGS_MAX_HEADER_LIST_SIZE:%uL\", value);\n        break;\n\n    case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS:\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 param QPACK_BLOCKED_STREAMS:%uL\", value);\n        break;\n\n    default:\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 param #%uL:%uL\", id, value);\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/v3/ngx_http_v3_tables.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_V3_TABLES_H_INCLUDED_\n#define _NGX_HTTP_V3_TABLES_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_str_t                     name;\n    ngx_str_t                     value;\n} ngx_http_v3_field_t;\n\n\ntypedef struct {\n    ngx_http_v3_field_t         **elts;\n    ngx_uint_t                    nelts;\n    ngx_uint_t                    base;\n    size_t                        size;\n    size_t                        capacity;\n} ngx_http_v3_dynamic_table_t;\n\n\nvoid ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c);\nngx_int_t ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic,\n    ngx_uint_t index, ngx_str_t *value);\nngx_int_t ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name,\n    ngx_str_t *value);\nngx_int_t ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity);\nngx_int_t ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index);\nngx_int_t ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id);\nngx_int_t ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc);\nngx_int_t ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index,\n    ngx_str_t *name, ngx_str_t *value);\nngx_int_t ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index,\n    ngx_str_t *name, ngx_str_t *value);\nngx_int_t ngx_http_v3_decode_insert_count(ngx_connection_t *c,\n    ngx_uint_t *insert_count);\nngx_int_t ngx_http_v3_check_insert_count(ngx_connection_t *c,\n    ngx_uint_t insert_count);\nngx_int_t ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id,\n    uint64_t value);\n\n\n#endif /* _NGX_HTTP_V3_TABLES_H_INCLUDED_ */\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;\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_n(&cmcf->listen, u.naddrs);\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    als = cmcf->listen.elts;\n\n    for (n = 0; n < u.naddrs; n++) {\n        ls[n] = ls[0];\n\n        ls[n].sockaddr = u.addrs[n].sockaddr;\n        ls[n].socklen = u.addrs[n].socklen;\n        ls[n].addr_text = u.addrs[n].name;\n        ls[n].wildcard = ngx_inet_wildcard(ls[n].sockaddr);\n\n        for (i = 0; i < cmcf->listen.nelts - u.naddrs + n; i++) {\n\n            if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,\n                                 ls[n].sockaddr, ls[n].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                               &ls[n].addr_text);\n            return NGX_CONF_ERROR;\n        }\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            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            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            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_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_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|NGX_SSL_TLSv1\n                          |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));\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) {\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_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 (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\n#define ngx_free          free\n\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_uint_t        eintr, eagain;\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#if (NGX_HAVE_AIO_SENDFILE)\n    ngx_uint_t        ebusy;\n    ngx_event_aio_t  *aio;\n#endif\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#if (NGX_HAVE_AIO_SENDFILE && NGX_SUPPRESS_WARN)\n    aio = NULL;\n    file = NULL;\n#endif\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_AIO_SENDFILE)\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_AIO_SENDFILE)\n            aio = file->file->aio;\n            flags = (aio && aio->preload_handler) ? SF_NODISKIO : 0;\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_AIO_SENDFILE)\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_AIO_SENDFILE)\n\n        if (ebusy) {\n            if (aio->event.active) {\n                /*\n                 * tolerate duplicate calls; they can happen due to subrequests\n                 * or multiple calls of the next body filter from a filter\n                 */\n\n                if (sent) {\n                    c->busy_count = 0;\n                }\n\n                return in;\n            }\n\n            if (sent == 0) {\n                c->busy_count++;\n\n                if (c->busy_count > 2) {\n                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                                  \"sendfile(%V) returned busy again\",\n                                  &file->file->name);\n\n                    c->busy_count = 0;\n                    aio->preload_handler = NULL;\n\n                    send = prev_send;\n                    continue;\n                }\n\n            } else {\n                c->busy_count = 0;\n            }\n\n            n = aio->preload_handler(file);\n\n            if (n > 0) {\n                send = prev_send + sent;\n                continue;\n            }\n\n            return in;\n        }\n\n        if (flags == SF_NODISKIO) {\n            c->busy_count = 0;\n        }\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    if (task->event.active && ctx->file == file) {\n        /*\n         * tolerate duplicate calls; they can happen due to subrequests\n         * or multiple calls of the next body filter from a filter\n         */\n\n        return NGX_DONE;\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_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\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    { 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        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        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 (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\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    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    ngx_new_binary = 0;\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        }\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                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            /* allow new processes to start */\n            ngx_msleep(100);\n\n            live = 1;\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            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    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                    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                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\n                live = 1;\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            live = 1;\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\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            }\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    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 (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 (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    /*\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    for (i = 0; i < cycle->listening.nelts; i++) {\n        ls[i].previous = NULL;\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 = 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                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    }\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"
  },
  {
    "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\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\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\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                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_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            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_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\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_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;\ntypedef ngx_rbtree_key_int_t  ngx_msec_int_t;\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 defined(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 defined(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 defined(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\n\n#if (NGX_HAVE_UDP_SENDMMSG)\n\nngx_int_t\nngx_sendmmsg(ngx_connection_t *c, struct mmsghdr *msgs, ngx_int_t count, int flags)\n{\n    ngx_int_t   n;\n    ngx_err_t   err;\n#if (NGX_DEBUG)\n    size_t      size;\n    ngx_int_t   i;\n#endif\n\neintr:\n\n    n = sendmmsg(c->fd, msgs, count, 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                           \"sendmmsg() not ready\");\n            return NGX_AGAIN;\n\n        case NGX_EINTR:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"sendmmsg() was interrupted\");\n            goto eintr;\n\n        default:\n            c->write->error = 1;\n            ngx_connection_error(c, err, \"sendmmsg() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n#if (NGX_DEBUG)\n    for (i = 0, size = 0; i < n; i++) {\n        size += msgs[i].msg_len;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"sendmmsg: %uz (%z)\", size, n);\n#endif\n\n    return n;\n}\n\n#endif\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/os/win32/nginx.rc",
    "content": "\n// Copyright (C) Igor Sysoev\n// Copyright (C) Nginx, Inc.\n\n\nnginx   icon  discardable  \"src\\\\os\\\\win32\\\\nginx.ico\"\n"
  },
  {
    "path": "src/os/win32/nginx_icon16.xpm",
    "content": "/* XPM */\nstatic char * nginx_xpm[] = {\n\"16 16 2 2\",\n/* colors */\n\"   c none\",\n\"GG c #009900\",\n/* pixels */\n\"                                \",\n\"        GGGGGGGGGGGGGGGG        \",\n\"        GGGGGGGGGGGGGGGG        \",\n\"      GGGGGGGGGGGGGGGGGGGG      \",\n\"      GGGGGG        GGGGGG      \",\n\"    GGGGGG            GGGGGG    \",\n\"    GGGGGG                      \",\n\"  GGGGGG      GGGGGGGGGGGGGGGG  \",\n\"  GGGGGG    GGGGGGGGGGGGGGGGGG  \",\n\"    GGGGGG    GGGGGGGGGGGGGG    \",\n\"    GGGGGG            GGGGGG    \",\n\"      GGGGGG        GGGGGG      \",\n\"      GGGGGGGGGGGGGGGGGGGG      \",\n\"        GGGGGGGGGGGGGGGG        \",\n\"        GGGGGGGGGGGGGGGG        \",\n\"                                \"\n};\n"
  },
  {
    "path": "src/os/win32/nginx_icon32.xpm",
    "content": "/* XPM */\nstatic char * nginx_xpm[] = {\n\"32 32 2 2\",\n/* colors */\n\"   c none\",\n\"GG c #009900\",\n/* pixels */\n\"                                                                \",\n\"                                                                \",\n\"                                                                \",\n\"                                                                \",\n\"                  GGGGGGGGGGGGGGGGGGGGGGGGGGGG                  \",\n\"                GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                \",\n\"                GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                \",\n\"              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG              \",\n\"              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG              \",\n\"            GGGGGGGGGG                    GGGGGGGGGG            \",\n\"            GGGGGGGGGG                    GGGGGGGGGG            \",\n\"          GGGGGGGGGG                        GGGGGGGGGG          \",\n\"          GGGGGGGGGG                        GGGGGGGGGG          \",\n\"        GGGGGGGGGG                                              \",\n\"        GGGGGGGGGG                                              \",\n\"      GGGGGGGGGG            GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG      \",\n\"      GGGGGGGGGG          GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG      \",\n\"        GGGGGGGGGG        GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG        \",\n\"        GGGGGGGGGG        GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG        \",\n\"          GGGGGGGGGG        GGGGGGGGGGGGGGGGGGGGGGGGGG          \",\n\"          GGGGGGGGGG                        GGGGGGGGGG          \",\n\"            GGGGGGGGGG                    GGGGGGGGGG            \",\n\"            GGGGGGGGGG                    GGGGGGGGGG            \",\n\"              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG              \",\n\"              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG              \",\n\"                GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                \",\n\"                GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                \",\n\"                  GGGGGGGGGGGGGGGGGGGGGGGGGGGG                  \",\n\"                                                                \",\n\"                                                                \",\n\"                                                                \",\n\"                                                                \"\n"
  },
  {
    "path": "src/os/win32/nginx_icon48.xpm",
    "content": "/* XPM */\nstatic char * nginx_xpm[] = {\n\"48 48 2 2\",\n/* colors */\n\"   c none\",\n\"GG c #009900\",\n/* pixels */\n\"                                                                                                \",\n\"                                                                                                \",\n\"                                                                                                \",\n\"                                                                                                \",\n\"                                                                                                \",\n\"                        GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                        \",\n\"                        GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                        \",\n\"                      GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                      \",\n\"                      GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                      \",\n\"                    GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                    \",\n\"                    GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                    \",\n\"                  GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                  \",\n\"                  GGGGGGGGGGGGGGGG                            GGGGGGGGGGGGGGGG                  \",\n\"                GGGGGGGGGGGGGGGG                                GGGGGGGGGGGGGGGG                \",\n\"                GGGGGGGGGGGGGGGG                                GGGGGGGGGGGGGGGG                \",\n\"              GGGGGGGGGGGGGGGG                                    GGGGGGGGGGGGGGGG              \",\n\"              GGGGGGGGGGGGGGGG                                    GGGGGGGGGGGGGGGG              \",\n\"            GGGGGGGGGGGGGGGG                                        GGGGGGGGGGGGGGGG            \",\n\"            GGGGGGGGGGGGGGGG                                        GGGGGGGGGGGGGGGG            \",\n\"          GGGGGGGGGGGGGGGG                                            GGGGGGGGGGGGGGGG          \",\n\"          GGGGGGGGGGGGGGGG                                                                      \",\n\"        GGGGGGGGGGGGGGGG                                                                        \",\n\"        GGGGGGGGGGGGGGGG                                                                        \",\n\"      GGGGGGGGGGGGGGGG                    GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG      \",\n\"      GGGGGGGGGGGGGGGG                  GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG      \",\n\"        GGGGGGGGGGGGGGGG              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG        \",\n\"        GGGGGGGGGGGGGGGG              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG        \",\n\"          GGGGGGGGGGGGGGGG            GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG          \",\n\"          GGGGGGGGGGGGGGGG              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG          \",\n\"            GGGGGGGGGGGGGGGG              GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG            \",\n\"            GGGGGGGGGGGGGGGG                                        GGGGGGGGGGGGGGGG            \",\n\"              GGGGGGGGGGGGGGGG                                    GGGGGGGGGGGGGGGG              \",\n\"              GGGGGGGGGGGGGGGG                                    GGGGGGGGGGGGGGGG              \",\n\"                GGGGGGGGGGGGGGGG                                GGGGGGGGGGGGGGGG                \",\n\"                GGGGGGGGGGGGGGGG                                GGGGGGGGGGGGGGGG                \",\n\"                  GGGGGGGGGGGGGGGG                            GGGGGGGGGGGGGGGG                  \",\n\"                  GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                  \",\n\"                    GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                    \",\n\"                    GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                    \",\n\"                      GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                      \",\n\"                      GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                      \",\n\"                        GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                        \",\n\"                        GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG                        \",\n\"                                                                                                \",\n\"                                                                                                \",\n\"                                                                                                \",\n\"                                                                                                \",\n\"                                                                                                \",\n"
  },
  {
    "path": "src/os/win32/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 *ngx_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 *ngx_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"
  },
  {
    "path": "src/os/win32/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\n#define ngx_free          free\n#define ngx_memalign(alignment, size, log)  ngx_alloc(size, log)\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/win32/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#define NGX_HAVE_ATOMIC_OPS   1\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 defined( __WATCOMC__ ) || defined( __BORLANDC__ ) || defined(__GNUC__)    \\\n    || ( _MSC_VER >= 1300 )\n\n/* the new SDK headers */\n\n#define ngx_atomic_cmp_set(lock, old, set)                                    \\\n    ((ngx_atomic_uint_t) InterlockedCompareExchange((long *) lock, set, old)  \\\n                         == old)\n\n#else\n\n/* the old MS VC6.0SP2 SDK headers */\n\n#define ngx_atomic_cmp_set(lock, old, set)                                    \\\n    (InterlockedCompareExchange((void **) lock, (void *) set, (void *) old)   \\\n     == (void *) old)\n\n#endif\n\n\n#define ngx_atomic_fetch_add(p, add) InterlockedExchangeAdd((long *) p, add)\n\n\n#define ngx_memory_barrier()\n\n\n#if defined( __BORLANDC__ ) || ( __WATCOMC__ < 1230 )\n\n/*\n * Borland C++ 5.5 (tasm32) and Open Watcom C prior to 1.3\n * do not understand the \"pause\" instruction\n */\n\n#define ngx_cpu_pause()\n#else\n#define ngx_cpu_pause()       __asm { pause }\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/win32/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\nchar *\nngx_dlerror(void)\n{\n    u_char         *p;\n    static u_char   errstr[NGX_MAX_ERROR_STR];\n\n    p = ngx_strerror(ngx_errno, errstr, NGX_MAX_ERROR_STR);\n    *p = '\\0';\n\n    return (char *) errstr;\n}\n"
  },
  {
    "path": "src/os/win32/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_HAVE_DLOPEN  1\n\n\n#define ngx_dlopen(path)           LoadLibrary((char *) path)\n#define ngx_dlopen_n               \"LoadLibrary()\"\n\n#define ngx_dlsym(handle, symbol)  (void *) GetProcAddress(handle, symbol)\n#define ngx_dlsym_n                \"GetProcAddress()\"\n\n#define ngx_dlclose(handle)        (FreeLibrary(handle) ? 0 : -1)\n#define ngx_dlclose_n              \"FreeLibrary()\"\n\n\nchar *ngx_dlerror(void);\n\n\n#endif /* _NGX_DLOPEN_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/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\nu_char *\nngx_strerror(ngx_err_t err, u_char *errstr, size_t size)\n{\n    u_int          len;\n    static u_long  lang = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);\n\n    if (size == 0) {\n        return errstr;\n    }\n\n    len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\n                        NULL, err, lang, (char *) errstr, size, NULL);\n\n    if (len == 0 && lang) {\n\n        /*\n         * Try to use English messages first and fallback to a language,\n         * based on locale: non-English Windows have no English messages\n         * at all.  This way allows to use English messages at least on\n         * Windows with MUI.\n         */\n\n        lang = 0;\n\n        len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\n                            NULL, err, lang, (char *) errstr, size, NULL);\n    }\n\n    if (len == 0) {\n        return ngx_snprintf(errstr, size,\n                            \"FormatMessage() error:(%d)\", GetLastError());\n    }\n\n    /* remove \".\\r\\n\\0\" */\n    while (errstr[len] == '\\0' || errstr[len] == CR\n           || errstr[len] == LF || errstr[len] == '.')\n    {\n        --len;\n    }\n\n    return &errstr[++len];\n}\n\n\nngx_int_t\nngx_strerror_init(void)\n{\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/os/win32/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 DWORD                      ngx_err_t;\n\n#define ngx_errno                  GetLastError()\n#define ngx_set_errno(err)         SetLastError(err)\n#define ngx_socket_errno           WSAGetLastError()\n#define ngx_set_socket_errno(err)  WSASetLastError(err)\n\n#define NGX_EPERM                  ERROR_ACCESS_DENIED\n#define NGX_ENOENT                 ERROR_FILE_NOT_FOUND\n#define NGX_ENOPATH                ERROR_PATH_NOT_FOUND\n#define NGX_ENOMEM                 ERROR_NOT_ENOUGH_MEMORY\n#define NGX_EACCES                 ERROR_ACCESS_DENIED\n/*\n * there are two EEXIST error codes:\n * ERROR_FILE_EXISTS used by CreateFile(CREATE_NEW),\n * and ERROR_ALREADY_EXISTS used by CreateDirectory();\n * MoveFile() uses both\n */\n#define NGX_EEXIST                 ERROR_ALREADY_EXISTS\n#define NGX_EEXIST_FILE            ERROR_FILE_EXISTS\n#define NGX_EXDEV                  ERROR_NOT_SAME_DEVICE\n#define NGX_ENOTDIR                ERROR_PATH_NOT_FOUND\n#define NGX_EISDIR                 ERROR_CANNOT_MAKE\n#define NGX_ENOSPC                 ERROR_DISK_FULL\n#define NGX_EPIPE                  EPIPE\n#define NGX_EAGAIN                 WSAEWOULDBLOCK\n#define NGX_EINPROGRESS            WSAEINPROGRESS\n#define NGX_ENOPROTOOPT            WSAENOPROTOOPT\n#define NGX_EOPNOTSUPP             WSAEOPNOTSUPP\n#define NGX_EADDRINUSE             WSAEADDRINUSE\n#define NGX_ECONNABORTED           WSAECONNABORTED\n#define NGX_ECONNRESET             WSAECONNRESET\n#define NGX_ENOTCONN               WSAENOTCONN\n#define NGX_ETIMEDOUT              WSAETIMEDOUT\n#define NGX_ECONNREFUSED           WSAECONNREFUSED\n#define NGX_ENAMETOOLONG           ERROR_BAD_PATHNAME\n#define NGX_ENETDOWN               WSAENETDOWN\n#define NGX_ENETUNREACH            WSAENETUNREACH\n#define NGX_EHOSTDOWN              WSAEHOSTDOWN\n#define NGX_EHOSTUNREACH           WSAEHOSTUNREACH\n#define NGX_ENOMOREFILES           ERROR_NO_MORE_FILES\n#define NGX_EILSEQ                 ERROR_NO_UNICODE_TRANSLATION\n#define NGX_ELOOP                  0\n#define NGX_EBADF                  WSAEBADF\n\n#define NGX_EALREADY               WSAEALREADY\n#define NGX_EINVAL                 WSAEINVAL\n#define NGX_EMFILE                 WSAEMFILE\n#define NGX_ENFILE                 WSAEMFILE\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/win32/ngx_event_log.c",
    "content": "/*\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#define NGX_MAX_ERROR_STR   2048\n\n\nvoid ngx_cdecl\nngx_event_log(ngx_err_t err, const char *fmt, ...)\n{\n    u_char         *p, *last;\n    long            types;\n    HKEY            key;\n    HANDLE          ev;\n    va_list         args;\n    u_char          text[NGX_MAX_ERROR_STR];\n    const char     *msgarg[9];\n    static u_char   netmsg[] = \"%SystemRoot%\\\\System32\\\\netmsg.dll\";\n\n    last = text + NGX_MAX_ERROR_STR;\n    p = text + GetModuleFileName(NULL, (char *) text, NGX_MAX_ERROR_STR - 50);\n\n    *p++ = ':';\n    ngx_linefeed(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 (p > last - NGX_LINEFEED_SIZE - 1) {\n        p = last - NGX_LINEFEED_SIZE - 1;\n    }\n\n    ngx_linefeed(p);\n\n    *p = '\\0';\n\n    /*\n     * we do not log errors here since we use\n     * Event Log only to log our own logs open errors\n     */\n\n    if (RegCreateKeyEx(HKEY_LOCAL_MACHINE,\n           \"SYSTEM\\\\CurrentControlSet\\\\Services\\\\EventLog\\\\Application\\\\nginx\",\n           0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &key, NULL)\n        != 0)\n    {\n        return;\n    }\n\n    if (RegSetValueEx(key, \"EventMessageFile\", 0, REG_EXPAND_SZ,\n                      netmsg, sizeof(netmsg) - 1)\n        != 0)\n    {\n        return;\n    }\n\n    types = EVENTLOG_ERROR_TYPE;\n\n    if (RegSetValueEx(key, \"TypesSupported\", 0, REG_DWORD,\n                      (u_char *) &types, sizeof(long))\n        != 0)\n    {\n        return;\n    }\n\n    RegCloseKey(key);\n\n    ev = RegisterEventSource(NULL, \"nginx\");\n\n    msgarg[0] = (char *) text;\n    msgarg[1] = NULL;\n    msgarg[2] = NULL;\n    msgarg[3] = NULL;\n    msgarg[4] = NULL;\n    msgarg[5] = NULL;\n    msgarg[6] = NULL;\n    msgarg[7] = NULL;\n    msgarg[8] = NULL;\n\n    /*\n     * the 3299 event id in netmsg.dll has the generic message format:\n     *     \"%1 %2 %3 %4 %5 %6 %7 %8 %9\"\n     */\n\n    ReportEvent(ev, EVENTLOG_ERROR_TYPE, 0, 3299, NULL, 9, 0, msgarg, NULL);\n\n    DeregisterEventSource(ev);\n}\n"
  },
  {
    "path": "src/os/win32/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#define NGX_UTF16_BUFLEN  256\n\nstatic ngx_int_t ngx_win32_check_filename(u_char *name, u_short *u,\n    size_t len);\nstatic u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len);\n\n\n/* FILE_FLAG_BACKUP_SEMANTICS allows to obtain a handle to a directory */\n\nngx_fd_t\nngx_open_file(u_char *name, u_long mode, u_long create, u_long access)\n{\n    size_t      len;\n    u_short    *u;\n    ngx_fd_t    fd;\n    ngx_err_t   err;\n    u_short     utf16[NGX_UTF16_BUFLEN];\n\n    len = NGX_UTF16_BUFLEN;\n    u = ngx_utf8_to_utf16(utf16, name, &len);\n\n    if (u == NULL) {\n        return INVALID_HANDLE_VALUE;\n    }\n\n    fd = INVALID_HANDLE_VALUE;\n\n    if (create == NGX_FILE_OPEN\n        && ngx_win32_check_filename(name, u, len) != NGX_OK)\n    {\n        goto failed;\n    }\n\n    fd = CreateFileW(u, mode,\n                     FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,\n                     NULL, create, FILE_FLAG_BACKUP_SEMANTICS, NULL);\n\nfailed:\n\n    if (u != utf16) {\n        err = ngx_errno;\n        ngx_free(u);\n        ngx_set_errno(err);\n    }\n\n    return fd;\n}\n\n\nssize_t\nngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)\n{\n    u_long      n;\n    ngx_err_t   err;\n    OVERLAPPED  ovlp, *povlp;\n\n    ovlp.Internal = 0;\n    ovlp.InternalHigh = 0;\n    ovlp.Offset = (u_long) offset;\n    ovlp.OffsetHigh = (u_long) (offset >> 32);\n    ovlp.hEvent = NULL;\n\n    povlp = &ovlp;\n\n    if (ReadFile(file->fd, buf, size, &n, povlp) == 0) {\n        err = ngx_errno;\n\n        if (err == ERROR_HANDLE_EOF) {\n            return 0;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, file->log, err,\n                      \"ReadFile() \\\"%s\\\" failed\", file->name.data);\n        return NGX_ERROR;\n    }\n\n    file->offset += n;\n\n    return n;\n}\n\n\nssize_t\nngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)\n{\n    u_long      n;\n    OVERLAPPED  ovlp, *povlp;\n\n    ovlp.Internal = 0;\n    ovlp.InternalHigh = 0;\n    ovlp.Offset = (u_long) offset;\n    ovlp.OffsetHigh = (u_long) (offset >> 32);\n    ovlp.hEvent = NULL;\n\n    povlp = &ovlp;\n\n    if (WriteFile(file->fd, buf, size, &n, povlp) == 0) {\n        ngx_log_error(NGX_LOG_ERR, file->log, ngx_errno,\n                      \"WriteFile() \\\"%s\\\" failed\", file->name.data);\n        return NGX_ERROR;\n    }\n\n    if (n != size) {\n        ngx_log_error(NGX_LOG_CRIT, file->log, 0,\n                      \"WriteFile() \\\"%s\\\" has written only %ul of %uz\",\n                      file->name.data, n, size);\n        return NGX_ERROR;\n    }\n\n    file->offset += n;\n\n    return n;\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    u_char   *buf, *prev;\n    size_t    size;\n    ssize_t   total, n;\n\n    total = 0;\n\n    while (cl) {\n        buf = cl->buf->pos;\n        prev = buf;\n        size = 0;\n\n        /* coalesce the neighbouring bufs */\n\n        while (cl && prev == cl->buf->pos) {\n            size += cl->buf->last - cl->buf->pos;\n            prev = cl->buf->last;\n            cl = cl->next;\n        }\n\n        n = ngx_write_file(file, buf, size, offset);\n\n        if (n == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        total += n;\n        offset += n;\n    }\n\n    return total;\n}\n\n\nssize_t\nngx_read_fd(ngx_fd_t fd, void *buf, size_t size)\n{\n    u_long  n;\n\n    if (ReadFile(fd, buf, size, &n, NULL) != 0) {\n        return (size_t) n;\n    }\n\n    return -1;\n}\n\n\nssize_t\nngx_write_fd(ngx_fd_t fd, void *buf, size_t size)\n{\n    u_long  n;\n\n    if (WriteFile(fd, buf, size, &n, NULL) != 0) {\n        return (size_t) n;\n    }\n\n    return -1;\n}\n\n\nssize_t\nngx_write_console(ngx_fd_t fd, void *buf, size_t size)\n{\n    u_long  n;\n\n    (void) CharToOemBuff(buf, buf, size);\n\n    if (WriteFile(fd, buf, size, &n, NULL) != 0) {\n        return (size_t) n;\n    }\n\n    return -1;\n}\n\n\nngx_err_t\nngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log)\n{\n    u_char             *name;\n    ngx_err_t           err;\n    ngx_uint_t          collision;\n    ngx_atomic_uint_t   num;\n\n    name = ngx_alloc(to->len + 1 + NGX_ATOMIC_T_LEN + 1 + sizeof(\"DELETE\"),\n                     log);\n    if (name == NULL) {\n        return NGX_ENOMEM;\n    }\n\n    ngx_memcpy(name, to->data, to->len);\n\n    collision = 0;\n\n    /* mutex_lock() (per cache or single ?) */\n\n    for ( ;; ) {\n        num = ngx_next_temp_number(collision);\n\n        ngx_sprintf(name + to->len, \".%0muA.DELETE%Z\", num);\n\n        if (MoveFile((const char *) to->data, (const char *) name) != 0) {\n            break;\n        }\n\n        collision = 1;\n\n        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,\n                      \"MoveFile() \\\"%s\\\" to \\\"%s\\\" failed\", to->data, name);\n    }\n\n    if (MoveFile((const char *) from->data, (const char *) to->data) == 0) {\n        err = ngx_errno;\n\n    } else {\n        err = 0;\n    }\n\n    if (DeleteFile((const char *) name) == 0) {\n        ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,\n                      \"DeleteFile() \\\"%s\\\" failed\", name);\n    }\n\n    /* mutex_unlock() */\n\n    ngx_free(name);\n\n    return err;\n}\n\n\nngx_int_t\nngx_file_info(u_char *file, ngx_file_info_t *sb)\n{\n    size_t                      len;\n    long                        rc;\n    u_short                    *u;\n    ngx_err_t                   err;\n    WIN32_FILE_ATTRIBUTE_DATA   fa;\n    u_short                     utf16[NGX_UTF16_BUFLEN];\n\n    len = NGX_UTF16_BUFLEN;\n\n    u = ngx_utf8_to_utf16(utf16, file, &len);\n\n    if (u == NULL) {\n        return NGX_FILE_ERROR;\n    }\n\n    rc = NGX_FILE_ERROR;\n\n    if (ngx_win32_check_filename(file, u, len) != NGX_OK) {\n        goto failed;\n    }\n\n    rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa);\n\n    sb->dwFileAttributes = fa.dwFileAttributes;\n    sb->ftCreationTime = fa.ftCreationTime;\n    sb->ftLastAccessTime = fa.ftLastAccessTime;\n    sb->ftLastWriteTime = fa.ftLastWriteTime;\n    sb->nFileSizeHigh = fa.nFileSizeHigh;\n    sb->nFileSizeLow = fa.nFileSizeLow;\n\nfailed:\n\n    if (u != utf16) {\n        err = ngx_errno;\n        ngx_free(u);\n        ngx_set_errno(err);\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s)\n{\n    uint64_t  intervals;\n    FILETIME  ft;\n\n    /* 116444736000000000 is commented in src/os/win32/ngx_time.c */\n\n    intervals = s * 10000000 + 116444736000000000;\n\n    ft.dwLowDateTime = (DWORD) intervals;\n    ft.dwHighDateTime = (DWORD) (intervals >> 32);\n\n    if (SetFileTime(fd, NULL, NULL, &ft) != 0) {\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    LARGE_INTEGER  size;\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    fm->handle = NULL;\n\n    size.QuadPart = fm->size;\n\n    if (SetFilePointerEx(fm->fd, size, NULL, FILE_BEGIN) == 0) {\n        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                      \"SetFilePointerEx(\\\"%s\\\", %uz) failed\",\n                      fm->name, fm->size);\n        goto failed;\n    }\n\n    if (SetEndOfFile(fm->fd) == 0) {\n        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                      \"SetEndOfFile() \\\"%s\\\" failed\", fm->name);\n        goto failed;\n    }\n\n    fm->handle = CreateFileMapping(fm->fd, NULL, PAGE_READWRITE,\n                                   (u_long) ((off_t) fm->size >> 32),\n                                   (u_long) ((off_t) fm->size & 0xffffffff),\n                                   NULL);\n    if (fm->handle == NULL) {\n        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                      \"CreateFileMapping(%s, %uz) failed\",\n                      fm->name, fm->size);\n        goto failed;\n    }\n\n    fm->addr = MapViewOfFile(fm->handle, FILE_MAP_WRITE, 0, 0, 0);\n\n    if (fm->addr != NULL) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                  \"MapViewOfFile(%uz) of file mapping \\\"%s\\\" failed\",\n                  fm->size, fm->name);\n\nfailed:\n\n    if (fm->handle) {\n        if (CloseHandle(fm->handle) == 0) {\n            ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,\n                          \"CloseHandle() of file mapping \\\"%s\\\" failed\",\n                          fm->name);\n        }\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    return NGX_ERROR;\n}\n\n\nvoid\nngx_close_file_mapping(ngx_file_mapping_t *fm)\n{\n    if (UnmapViewOfFile(fm->addr) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,\n                      \"UnmapViewOfFile(%p) of file mapping \\\"%s\\\" failed\",\n                      fm->addr, &fm->name);\n    }\n\n    if (CloseHandle(fm->handle) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,\n                      \"CloseHandle() of file mapping \\\"%s\\\" failed\",\n                      &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\nu_char *\nngx_realpath(u_char *path, u_char *resolved)\n{\n    /* STUB */\n    return path;\n}\n\n\nngx_int_t\nngx_open_dir(ngx_str_t *name, ngx_dir_t *dir)\n{\n    u_char     *pattern, *p;\n    ngx_err_t   err;\n\n    pattern = malloc(name->len + 3);\n    if (pattern == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(pattern, name->data, name->len);\n\n    *p++ = '/';\n    *p++ = '*';\n    *p = '\\0';\n\n    dir->dir = FindFirstFile((const char *) pattern, &dir->finddata);\n\n    if (dir->dir == INVALID_HANDLE_VALUE) {\n        err = ngx_errno;\n        ngx_free(pattern);\n        ngx_set_errno(err);\n        return NGX_ERROR;\n    }\n\n    ngx_free(pattern);\n\n    dir->valid_info = 1;\n    dir->ready = 1;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_read_dir(ngx_dir_t *dir)\n{\n    if (dir->ready) {\n        dir->ready = 0;\n        return NGX_OK;\n    }\n\n    if (FindNextFile(dir->dir, &dir->finddata) != 0) {\n        dir->type = 1;\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_close_dir(ngx_dir_t *dir)\n{\n    if (FindClose(dir->dir) == 0) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_open_glob(ngx_glob_t *gl)\n{\n    u_char     *p;\n    size_t      len;\n    ngx_err_t   err;\n\n    gl->dir = FindFirstFile((const char *) gl->pattern, &gl->finddata);\n\n    if (gl->dir == INVALID_HANDLE_VALUE) {\n\n        err = ngx_errno;\n\n        if ((err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)\n             && gl->test)\n        {\n            gl->no_match = 1;\n            return NGX_OK;\n        }\n\n        return NGX_ERROR;\n    }\n\n    for (p = gl->pattern; *p; p++) {\n        if (*p == '/') {\n            gl->last = p + 1 - gl->pattern;\n        }\n    }\n\n    len = ngx_strlen(gl->finddata.cFileName);\n    gl->name.len = gl->last + len;\n\n    gl->name.data = ngx_alloc(gl->name.len + 1, gl->log);\n    if (gl->name.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(gl->name.data, gl->pattern, gl->last);\n    ngx_cpystrn(gl->name.data + gl->last, (u_char *) gl->finddata.cFileName,\n                len + 1);\n\n    gl->ready = 1;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_read_glob(ngx_glob_t *gl, ngx_str_t *name)\n{\n    size_t     len;\n    ngx_err_t  err;\n\n    if (gl->no_match) {\n        return NGX_DONE;\n    }\n\n    if (gl->ready) {\n        *name = gl->name;\n\n        gl->ready = 0;\n        return NGX_OK;\n    }\n\n    ngx_free(gl->name.data);\n    gl->name.data = NULL;\n\n    if (FindNextFile(gl->dir, &gl->finddata) != 0) {\n\n        len = ngx_strlen(gl->finddata.cFileName);\n        gl->name.len = gl->last + len;\n\n        gl->name.data = ngx_alloc(gl->name.len + 1, gl->log);\n        if (gl->name.data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(gl->name.data, gl->pattern, gl->last);\n        ngx_cpystrn(gl->name.data + gl->last, (u_char *) gl->finddata.cFileName,\n                    len + 1);\n\n        *name = gl->name;\n\n        return NGX_OK;\n    }\n\n    err = ngx_errno;\n\n    if (err == NGX_ENOMOREFILES) {\n        return NGX_DONE;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, gl->log, err,\n                  \"FindNextFile(%s) failed\", gl->pattern);\n\n    return NGX_ERROR;\n}\n\n\nvoid\nngx_close_glob(ngx_glob_t *gl)\n{\n    if (gl->name.data) {\n        ngx_free(gl->name.data);\n    }\n\n    if (gl->dir == INVALID_HANDLE_VALUE) {\n        return;\n    }\n\n    if (FindClose(gl->dir) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, gl->log, ngx_errno,\n                      \"FindClose(%s) failed\", gl->pattern);\n    }\n}\n\n\nngx_int_t\nngx_de_info(u_char *name, ngx_dir_t *dir)\n{\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_de_link_info(u_char *name, ngx_dir_t *dir)\n{\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_read_ahead(ngx_fd_t fd, size_t n)\n{\n    return ~NGX_FILE_ERROR;\n}\n\n\nngx_int_t\nngx_directio_on(ngx_fd_t fd)\n{\n    return ~NGX_FILE_ERROR;\n}\n\n\nngx_int_t\nngx_directio_off(ngx_fd_t fd)\n{\n    return ~NGX_FILE_ERROR;\n}\n\n\nsize_t\nngx_fs_bsize(u_char *name)\n{\n    u_char  root[4];\n    u_long  sc, bs, nfree, ncl;\n\n    if (name[2] == ':') {\n        ngx_cpystrn(root, name, 4);\n        name = root;\n    }\n\n    if (GetDiskFreeSpace((const char *) name, &sc, &bs, &nfree, &ncl) == 0) {\n        return 512;\n    }\n\n    return sc * bs;\n}\n\n\noff_t\nngx_fs_available(u_char *name)\n{\n    ULARGE_INTEGER  navail;\n\n    if (GetDiskFreeSpaceEx((const char *) name, &navail, NULL, NULL) == 0) {\n        return NGX_MAX_OFF_T_VALUE;\n    }\n\n    return (off_t) navail.QuadPart;\n}\n\n\nstatic ngx_int_t\nngx_win32_check_filename(u_char *name, u_short *u, size_t len)\n{\n    u_char     *p, ch;\n    u_long      n;\n    u_short    *lu;\n    ngx_err_t   err;\n    enum {\n        sw_start = 0,\n        sw_normal,\n        sw_after_slash,\n        sw_after_colon,\n        sw_after_dot\n    } state;\n\n    /* check for NTFS streams (\":\"), trailing dots and spaces */\n\n    lu = NULL;\n    state = sw_start;\n\n    for (p = name; *p; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        case sw_start:\n\n            /*\n             * skip till first \"/\" to allow paths starting with drive and\n             * relative path, like \"c:html/\"\n             */\n\n            if (ch == '/' || ch == '\\\\') {\n                state = sw_after_slash;\n            }\n\n            break;\n\n        case sw_normal:\n\n            if (ch == ':') {\n                state = sw_after_colon;\n                break;\n            }\n\n            if (ch == '.' || ch == ' ') {\n                state = sw_after_dot;\n                break;\n            }\n\n            if (ch == '/' || ch == '\\\\') {\n                state = sw_after_slash;\n                break;\n            }\n\n            break;\n\n        case sw_after_slash:\n\n            if (ch == '/' || ch == '\\\\') {\n                break;\n            }\n\n            if (ch == '.') {\n                break;\n            }\n\n            if (ch == ':') {\n                state = sw_after_colon;\n                break;\n            }\n\n            state = sw_normal;\n            break;\n\n        case sw_after_colon:\n\n            if (ch == '/' || ch == '\\\\') {\n                state = sw_after_slash;\n                break;\n            }\n\n            goto invalid;\n\n        case sw_after_dot:\n\n            if (ch == '/' || ch == '\\\\') {\n                goto invalid;\n            }\n\n            if (ch == ':') {\n                goto invalid;\n            }\n\n            if (ch == '.' || ch == ' ') {\n                break;\n            }\n\n            state = sw_normal;\n            break;\n        }\n    }\n\n    if (state == sw_after_dot) {\n        goto invalid;\n    }\n\n    /* check if long name match */\n\n    lu = malloc(len * 2);\n    if (lu == NULL) {\n        return NGX_ERROR;\n    }\n\n    n = GetLongPathNameW(u, lu, len);\n\n    if (n == 0) {\n        goto failed;\n    }\n\n    if (n != len - 1 || _wcsicmp(u, lu) != 0) {\n        goto invalid;\n    }\n\n    ngx_free(lu);\n\n    return NGX_OK;\n\ninvalid:\n\n    ngx_set_errno(NGX_ENOENT);\n\nfailed:\n\n    if (lu) {\n        err = ngx_errno;\n        ngx_free(lu);\n        ngx_set_errno(err);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic u_short *\nngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len)\n{\n    u_char    *p;\n    u_short   *u, *last;\n    uint32_t   n;\n\n    p = utf8;\n    u = utf16;\n    last = utf16 + *len;\n\n    while (u < last) {\n\n        if (*p < 0x80) {\n            *u++ = (u_short) *p;\n\n            if (*p == 0) {\n                *len = u - utf16;\n                return utf16;\n            }\n\n            p++;\n\n            continue;\n        }\n\n        if (u + 1 == last) {\n            *len = u - utf16;\n            break;\n        }\n\n        n = ngx_utf8_decode(&p, 4);\n\n        if (n > 0x10ffff) {\n            ngx_set_errno(NGX_EILSEQ);\n            return NULL;\n        }\n\n        if (n > 0xffff) {\n            n -= 0x10000;\n            *u++ = (u_short) (0xd800 + (n >> 10));\n            *u++ = (u_short) (0xdc00 + (n & 0x03ff));\n            continue;\n        }\n\n        *u++ = (u_short) n;\n    }\n\n    /* the given buffer is not enough, allocate a new one */\n\n    u = malloc(((p - utf8) + ngx_strlen(p) + 1) * sizeof(u_short));\n    if (u == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(u, utf16, *len * 2);\n\n    utf16 = u;\n    u += *len;\n\n    for ( ;; ) {\n\n        if (*p < 0x80) {\n            *u++ = (u_short) *p;\n\n            if (*p == 0) {\n                *len = u - utf16;\n                return utf16;\n            }\n\n            p++;\n\n            continue;\n        }\n\n        n = ngx_utf8_decode(&p, 4);\n\n        if (n > 0x10ffff) {\n            ngx_free(utf16);\n            ngx_set_errno(NGX_EILSEQ);\n            return NULL;\n        }\n\n        if (n > 0xffff) {\n            n -= 0x10000;\n            *u++ = (u_short) (0xd800 + (n >> 10));\n            *u++ = (u_short) (0xdc00 + (n & 0x03ff));\n            continue;\n        }\n\n        *u++ = (u_short) n;\n    }\n\n    /* unreachable */\n}\n"
  },
  {
    "path": "src/os/win32/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 HANDLE                      ngx_fd_t;\ntypedef BY_HANDLE_FILE_INFORMATION  ngx_file_info_t;\ntypedef uint64_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    HANDLE                          handle;\n    ngx_log_t                      *log;\n} ngx_file_mapping_t;\n\n\ntypedef struct {\n    HANDLE                          dir;\n    WIN32_FIND_DATA                 finddata;\n\n    unsigned                        valid_info:1;\n    unsigned                        type:1;\n    unsigned                        ready:1;\n} ngx_dir_t;\n\n\ntypedef struct {\n    HANDLE                          dir;\n    WIN32_FIND_DATA                 finddata;\n\n    unsigned                        ready:1;\n    unsigned                        test:1;\n    unsigned                        no_match:1;\n\n    u_char                         *pattern;\n    ngx_str_t                       name;\n    size_t                          last;\n    ngx_log_t                      *log;\n} ngx_glob_t;\n\n\n\n/* INVALID_FILE_ATTRIBUTES is specified but not defined at least in MSVC6SP2 */\n#ifndef INVALID_FILE_ATTRIBUTES\n#define INVALID_FILE_ATTRIBUTES     0xffffffff\n#endif\n\n/* INVALID_SET_FILE_POINTER is not defined at least in MSVC6SP2 */\n#ifndef INVALID_SET_FILE_POINTER\n#define INVALID_SET_FILE_POINTER    0xffffffff\n#endif\n\n\n#define NGX_INVALID_FILE            INVALID_HANDLE_VALUE\n#define NGX_FILE_ERROR              0\n\n\nngx_fd_t ngx_open_file(u_char *name, u_long mode, u_long create, u_long access);\n#define ngx_open_file_n             \"CreateFile()\"\n\n#define NGX_FILE_RDONLY             GENERIC_READ\n#define NGX_FILE_WRONLY             GENERIC_WRITE\n#define NGX_FILE_RDWR               GENERIC_READ|GENERIC_WRITE\n#define NGX_FILE_APPEND             FILE_APPEND_DATA|SYNCHRONIZE\n#define NGX_FILE_NONBLOCK           0\n\n#define NGX_FILE_CREATE_OR_OPEN     OPEN_ALWAYS\n#define NGX_FILE_OPEN               OPEN_EXISTING\n#define NGX_FILE_TRUNCATE           CREATE_ALWAYS\n\n#define NGX_FILE_DEFAULT_ACCESS     0\n#define NGX_FILE_OWNER_ACCESS       0\n\n\n#define ngx_open_tempfile(name, persistent, access)                          \\\n    CreateFile((const char *) name,                                          \\\n               GENERIC_READ|GENERIC_WRITE,                                   \\\n               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,           \\\n               NULL,                                                         \\\n               CREATE_NEW,                                                   \\\n               persistent ? 0:                                               \\\n                   FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE,       \\\n               NULL);\n\n#define ngx_open_tempfile_n         \"CreateFile()\"\n\n\n#define ngx_close_file              CloseHandle\n#define ngx_close_file_n            \"CloseHandle()\"\n\n\nssize_t ngx_read_fd(ngx_fd_t fd, void *buf, size_t size);\n#define ngx_read_fd_n               \"ReadFile()\"\n\n\nssize_t ngx_write_fd(ngx_fd_t fd, void *buf, size_t size);\n#define ngx_write_fd_n              \"WriteFile()\"\n\n\nssize_t ngx_write_console(ngx_fd_t fd, void *buf, size_t size);\n\n\n#define ngx_linefeed(p)             *p++ = CR; *p++ = LF;\n#define NGX_LINEFEED_SIZE           2\n#define NGX_LINEFEED                CRLF\n\n\n#define ngx_delete_file(name)       DeleteFile((const char *) name)\n#define ngx_delete_file_n           \"DeleteFile()\"\n\n\n#define ngx_rename_file(o, n)       MoveFile((const char *) o, (const char *) n)\n#define ngx_rename_file_n           \"MoveFile()\"\nngx_err_t ngx_win32_rename_file(ngx_str_t *from, ngx_str_t *to, ngx_log_t *log);\n\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         \"SetFileTime()\"\n\n\nngx_int_t ngx_file_info(u_char *filename, ngx_file_info_t *fi);\n#define ngx_file_info_n             \"GetFileAttributesEx()\"\n\n\n#define ngx_fd_info(fd, fi)         GetFileInformationByHandle(fd, fi)\n#define ngx_fd_info_n               \"GetFileInformationByHandle()\"\n\n\n#define ngx_link_info(name, fi)     ngx_file_info(name, fi)\n#define ngx_link_info_n             \"GetFileAttributesEx()\"\n\n\n#define ngx_is_dir(fi)                                                       \\\n    (((fi)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)\n#define ngx_is_file(fi)                                                      \\\n    (((fi)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)\n#define ngx_is_link(fi)     0\n#define ngx_is_exec(fi)     0\n\n#define ngx_file_access(fi) 0\n\n#define ngx_file_size(fi)                                                    \\\n    (((off_t) (fi)->nFileSizeHigh << 32) | (fi)->nFileSizeLow)\n#define ngx_file_fs_size(fi)        ngx_file_size(fi)\n\n#define ngx_file_uniq(fi)   (*(ngx_file_uniq_t *) &(fi)->nFileIndexHigh)\n\n\n/* 116444736000000000 is commented in src/os/win32/ngx_time.c */\n\n#define ngx_file_mtime(fi)                                                   \\\n (time_t) (((((unsigned __int64) (fi)->ftLastWriteTime.dwHighDateTime << 32) \\\n                               | (fi)->ftLastWriteTime.dwLowDateTime)        \\\n                                          - 116444736000000000) / 10000000)\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\nu_char *ngx_realpath(u_char *path, u_char *resolved);\n#define ngx_realpath_n              \"\"\n#define ngx_getcwd(buf, size)       GetCurrentDirectory(size, (char *) buf)\n#define ngx_getcwd_n                \"GetCurrentDirectory()\"\n#define ngx_path_separator(c)       ((c) == '/' || (c) == '\\\\')\n\n#define NGX_HAVE_MAX_PATH           1\n#define NGX_MAX_PATH                MAX_PATH\n\n\nngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir);\n#define ngx_open_dir_n              \"FindFirstFile()\"\n\n\nngx_int_t ngx_read_dir(ngx_dir_t *dir);\n#define ngx_read_dir_n              \"FindNextFile()\"\n\n\nngx_int_t ngx_close_dir(ngx_dir_t *dir);\n#define ngx_close_dir_n             \"FindClose()\"\n\n\n#define ngx_create_dir(name, access) CreateDirectory((const char *) name, NULL)\n#define ngx_create_dir_n            \"CreateDirectory()\"\n\n\n#define ngx_delete_dir(name)        RemoveDirectory((const char *) name)\n#define ngx_delete_dir_n            \"RemoveDirectory()\"\n\n\n#define ngx_dir_access(a)           (a)\n\n\n#define ngx_de_name(dir)            ((u_char *) (dir)->finddata.cFileName)\n#define ngx_de_namelen(dir)         ngx_strlen((dir)->finddata.cFileName)\n\nngx_int_t ngx_de_info(u_char *name, ngx_dir_t *dir);\n#define ngx_de_info_n               \"dummy()\"\n\nngx_int_t ngx_de_link_info(u_char *name, ngx_dir_t *dir);\n#define ngx_de_link_info_n          \"dummy()\"\n\n#define ngx_de_is_dir(dir)                                                   \\\n    (((dir)->finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)\n#define ngx_de_is_file(dir)                                                  \\\n    (((dir)->finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)\n#define ngx_de_is_link(dir)         0\n#define ngx_de_access(dir)          0\n#define ngx_de_size(dir)                                                     \\\n  (((off_t) (dir)->finddata.nFileSizeHigh << 32) | (dir)->finddata.nFileSizeLow)\n#define ngx_de_fs_size(dir)         ngx_de_size(dir)\n\n/* 116444736000000000 is commented in src/os/win32/ngx_time.c */\n\n#define ngx_de_mtime(dir)                                                    \\\n    (time_t) (((((unsigned __int64)                                          \\\n                     (dir)->finddata.ftLastWriteTime.dwHighDateTime << 32)   \\\n                      | (dir)->finddata.ftLastWriteTime.dwLowDateTime)       \\\n                                          - 116444736000000000) / 10000000)\n\n\nngx_int_t ngx_open_glob(ngx_glob_t *gl);\n#define ngx_open_glob_n             \"FindFirstFile()\"\n\nngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name);\nvoid ngx_close_glob(ngx_glob_t *gl);\n\n\nssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset);\n#define ngx_read_file_n             \"ReadFile()\"\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\nngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n);\n#define ngx_read_ahead_n            \"ngx_read_ahead_n\"\n\nngx_int_t ngx_directio_on(ngx_fd_t fd);\n#define ngx_directio_on_n           \"ngx_directio_on_n\"\n\nngx_int_t ngx_directio_off(ngx_fd_t fd);\n#define ngx_directio_off_n          \"ngx_directio_off_n\"\n\nsize_t ngx_fs_bsize(u_char *name);\noff_t ngx_fs_available(u_char *name);\n\n\n#define ngx_stdout               GetStdHandle(STD_OUTPUT_HANDLE)\n#define ngx_stderr               GetStdHandle(STD_ERROR_HANDLE)\n#define ngx_set_stderr(fd)       SetStdHandle(STD_ERROR_HANDLE, fd)\n#define ngx_set_stderr_n         \"SetStdHandle(STD_ERROR_HANDLE)\"\n\n\n#endif /* _NGX_FILES_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/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_signal_process(ngx_cycle_t *cycle, char *sig, ngx_pid_t pid);\n\nssize_t ngx_wsarecv(ngx_connection_t *c, u_char *buf, size_t size);\nssize_t ngx_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size);\nssize_t ngx_udp_wsarecv(ngx_connection_t *c, u_char *buf, size_t size);\nssize_t ngx_udp_overlapped_wsarecv(ngx_connection_t *c, u_char *buf,\n    size_t size);\nssize_t ngx_wsarecv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit);\nssize_t ngx_wsasend(ngx_connection_t *c, u_char *buf, size_t size);\nssize_t ngx_overlapped_wsasend(ngx_connection_t *c, u_char *buf, size_t size);\nngx_chain_t *ngx_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\nngx_chain_t *ngx_overlapped_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\nvoid ngx_cdecl ngx_event_log(ngx_err_t err, const char *fmt, ...);\n\n\nextern ngx_os_io_t  ngx_os_io;\nextern ngx_uint_t   ngx_ncpu;\nextern ngx_uint_t   ngx_max_wsabufs;\nextern ngx_int_t    ngx_max_sockets;\nextern ngx_uint_t   ngx_inherited_nonblocking;\nextern ngx_uint_t   ngx_tcp_nodelay_and_tcp_nopush;\nextern ngx_uint_t   ngx_win32_version;\nextern char         ngx_unique[];\n\n\n#endif /* _NGX_OS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/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\n\nint              ngx_argc;\nchar           **ngx_argv;\nchar           **ngx_os_argv;\n\nngx_int_t        ngx_last_process;\nngx_process_t    ngx_processes[NGX_MAX_PROCESSES];\n\n\nngx_pid_t\nngx_spawn_process(ngx_cycle_t *cycle, char *name, ngx_int_t respawn)\n{\n    u_long          rc, n, code;\n    ngx_int_t       s;\n    ngx_pid_t       pid;\n    ngx_exec_ctx_t  ctx;\n    HANDLE          events[2];\n    char            file[MAX_PATH + 1];\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].handle == NULL) {\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 = GetModuleFileName(NULL, file, MAX_PATH);\n\n    if (n == 0) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"GetModuleFileName() failed\");\n        return NGX_INVALID_PID;\n    }\n\n    file[n] = '\\0';\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                   \"GetModuleFileName: \\\"%s\\\"\", file);\n\n    ctx.path = file;\n    ctx.name = name;\n    ctx.args = GetCommandLine();\n    ctx.argv = NULL;\n    ctx.envp = NULL;\n\n    pid = ngx_execute(cycle, &ctx);\n\n    if (pid == NGX_INVALID_PID) {\n        return pid;\n    }\n\n    ngx_memzero(&ngx_processes[s], sizeof(ngx_process_t));\n\n    ngx_processes[s].handle = ctx.child;\n    ngx_processes[s].pid = pid;\n    ngx_processes[s].name = name;\n\n    ngx_sprintf(ngx_processes[s].term_event, \"ngx_%s_term_%P%Z\", name, pid);\n    ngx_sprintf(ngx_processes[s].quit_event, \"ngx_%s_quit_%P%Z\", name, pid);\n    ngx_sprintf(ngx_processes[s].reopen_event, \"ngx_%s_reopen_%P%Z\",\n                name, pid);\n\n    events[0] = ngx_master_process_event;\n    events[1] = ctx.child;\n\n    rc = WaitForMultipleObjects(2, events, 0, 5000);\n\n    ngx_time_update();\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                   \"WaitForMultipleObjects: %ul\", rc);\n\n    switch (rc) {\n\n    case WAIT_OBJECT_0:\n\n        ngx_processes[s].term = OpenEvent(EVENT_MODIFY_STATE, 0,\n                                          (char *) ngx_processes[s].term_event);\n        if (ngx_processes[s].term == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"OpenEvent(\\\"%s\\\") failed\",\n                          ngx_processes[s].term_event);\n            goto failed;\n        }\n\n        ngx_processes[s].quit = OpenEvent(EVENT_MODIFY_STATE, 0,\n                                          (char *) ngx_processes[s].quit_event);\n        if (ngx_processes[s].quit == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"OpenEvent(\\\"%s\\\") failed\",\n                          ngx_processes[s].quit_event);\n            goto failed;\n        }\n\n        ngx_processes[s].reopen = OpenEvent(EVENT_MODIFY_STATE, 0,\n                                       (char *) ngx_processes[s].reopen_event);\n        if (ngx_processes[s].reopen == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"OpenEvent(\\\"%s\\\") failed\",\n                          ngx_processes[s].reopen_event);\n            goto failed;\n        }\n\n        if (ResetEvent(ngx_master_process_event) == 0) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"ResetEvent(\\\"%s\\\") failed\",\n                          ngx_master_process_event_name);\n            goto failed;\n        }\n\n        break;\n\n    case WAIT_OBJECT_0 + 1:\n        if (GetExitCodeProcess(ctx.child, &code) == 0) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"GetExitCodeProcess(%P) failed\", pid);\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"%s process %P exited with code %Xl\",\n                      name, pid, code);\n\n        goto failed;\n\n    case WAIT_TIMEOUT:\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"the event \\\"%s\\\" was not signaled for 5s\",\n                      ngx_master_process_event_name);\n        goto failed;\n\n    case WAIT_FAILED:\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"WaitForSingleObject(\\\"%s\\\") failed\",\n                      ngx_master_process_event_name);\n\n        goto failed;\n    }\n\n    if (respawn >= 0) {\n        return pid;\n    }\n\n    switch (respawn) {\n\n    case NGX_PROCESS_RESPAWN:\n        ngx_processes[s].just_spawn = 0;\n        break;\n\n    case NGX_PROCESS_JUST_RESPAWN:\n        ngx_processes[s].just_spawn = 1;\n        break;\n    }\n\n    if (s == ngx_last_process) {\n        ngx_last_process++;\n    }\n\n    return pid;\n\nfailed:\n\n    if (ngx_processes[s].reopen) {\n        ngx_close_handle(ngx_processes[s].reopen);\n    }\n\n    if (ngx_processes[s].quit) {\n        ngx_close_handle(ngx_processes[s].quit);\n    }\n\n    if (ngx_processes[s].term) {\n        ngx_close_handle(ngx_processes[s].term);\n    }\n\n    TerminateProcess(ngx_processes[s].handle, 2);\n\n    if (ngx_processes[s].handle) {\n        ngx_close_handle(ngx_processes[s].handle);\n        ngx_processes[s].handle = NULL;\n    }\n\n    return NGX_INVALID_PID;\n}\n\n\nngx_pid_t\nngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)\n{\n    STARTUPINFO          si;\n    PROCESS_INFORMATION  pi;\n\n    ngx_memzero(&si, sizeof(STARTUPINFO));\n    si.cb = sizeof(STARTUPINFO);\n\n    ngx_memzero(&pi, sizeof(PROCESS_INFORMATION));\n\n    if (CreateProcess(ctx->path, ctx->args,\n                      NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)\n        == 0)\n    {\n        ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno,\n                      \"CreateProcess(\\\"%s\\\") failed\", ngx_argv[0]);\n\n        return 0;\n    }\n\n    ctx->child = pi.hProcess;\n\n    if (CloseHandle(pi.hThread) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CloseHandle(pi.hThread) failed\");\n    }\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,\n                  \"start %s process %P\", ctx->name, pi.dwProcessId);\n\n    return pi.dwProcessId;\n}\n"
  },
  {
    "path": "src/os/win32/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\ntypedef DWORD               ngx_pid_t;\n#define NGX_INVALID_PID     0\n\n\n#define ngx_getpid          GetCurrentProcessId\n#define ngx_getppid()       0\n#define ngx_log_pid         ngx_pid\n\n\n#define NGX_PROCESS_SYNC_NAME                                                 \\\n    (sizeof(\"ngx_cache_manager_mutex_\") + NGX_INT32_LEN)\n\n\ntypedef uint64_t            ngx_cpuset_t;\n\n\ntypedef struct {\n    HANDLE                  handle;\n    ngx_pid_t               pid;\n    char                   *name;\n\n    HANDLE                  term;\n    HANDLE                  quit;\n    HANDLE                  reopen;\n\n    u_char                  term_event[NGX_PROCESS_SYNC_NAME];\n    u_char                  quit_event[NGX_PROCESS_SYNC_NAME];\n    u_char                  reopen_event[NGX_PROCESS_SYNC_NAME];\n\n    unsigned                just_spawn:1;\n    unsigned                exiting:1;\n} ngx_process_t;\n\n\ntypedef struct {\n    char                   *path;\n    char                   *name;\n    char                   *args;\n    char *const            *argv;\n    char *const            *envp;\n    HANDLE                  child;\n} ngx_exec_ctx_t;\n\n\nngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, char *name, ngx_int_t respawn);\nngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx);\n\n#define ngx_debug_point()\n#define ngx_sched_yield()   SwitchToThread()\n\n\n#define NGX_MAX_PROCESSES         (MAXIMUM_WAIT_OBJECTS - 4)\n\n#define NGX_PROCESS_RESPAWN       -2\n#define NGX_PROCESS_JUST_RESPAWN  -3\n\n\nextern int                  ngx_argc;\nextern char               **ngx_argv;\nextern char               **ngx_os_argv;\n\nextern ngx_int_t            ngx_last_process;\nextern ngx_process_t        ngx_processes[NGX_MAX_PROCESSES];\n\nextern ngx_pid_t            ngx_pid;\nextern ngx_pid_t            ngx_parent;\n\n\n#endif /* _NGX_PROCESS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/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 <nginx.h>\n\n\nstatic void ngx_console_init(ngx_cycle_t *cycle);\nstatic int __stdcall ngx_console_handler(u_long type);\nstatic ngx_int_t ngx_create_signal_events(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t type);\nstatic void ngx_reopen_worker_processes(ngx_cycle_t *cycle);\nstatic void ngx_quit_worker_processes(ngx_cycle_t *cycle, ngx_uint_t old);\nstatic void ngx_terminate_worker_processes(ngx_cycle_t *cycle);\nstatic ngx_uint_t ngx_reap_worker(ngx_cycle_t *cycle, HANDLE h);\nstatic void ngx_master_process_exit(ngx_cycle_t *cycle);\nstatic void ngx_worker_process_cycle(ngx_cycle_t *cycle, char *mevn);\nstatic void ngx_worker_process_exit(ngx_cycle_t *cycle);\nstatic ngx_thread_value_t __stdcall ngx_worker_thread(void *data);\nstatic ngx_thread_value_t __stdcall ngx_cache_manager_thread(void *data);\nstatic void ngx_cache_manager_process_handler(void);\nstatic ngx_thread_value_t __stdcall ngx_cache_loader_thread(void *data);\n\n\nngx_uint_t     ngx_process;\nngx_uint_t     ngx_worker;\nngx_pid_t      ngx_pid;\nngx_pid_t      ngx_parent;\n\nngx_uint_t     ngx_inherited;\nngx_pid_t      ngx_new_binary;\n\nsig_atomic_t   ngx_terminate;\nsig_atomic_t   ngx_quit;\nsig_atomic_t   ngx_reopen;\nsig_atomic_t   ngx_reconfigure;\nngx_uint_t     ngx_exiting;\n\n\nHANDLE         ngx_master_process_event;\nchar           ngx_master_process_event_name[NGX_PROCESS_SYNC_NAME];\n\nstatic HANDLE  ngx_stop_event;\nstatic char    ngx_stop_event_name[NGX_PROCESS_SYNC_NAME];\nstatic HANDLE  ngx_quit_event;\nstatic char    ngx_quit_event_name[NGX_PROCESS_SYNC_NAME];\nstatic HANDLE  ngx_reopen_event;\nstatic char    ngx_reopen_event_name[NGX_PROCESS_SYNC_NAME];\nstatic HANDLE  ngx_reload_event;\nstatic char    ngx_reload_event_name[NGX_PROCESS_SYNC_NAME];\n\nHANDLE         ngx_cache_manager_mutex;\nchar           ngx_cache_manager_mutex_name[NGX_PROCESS_SYNC_NAME];\nHANDLE         ngx_cache_manager_event;\n\n\nvoid\nngx_master_process_cycle(ngx_cycle_t *cycle)\n{\n    u_long      nev, ev, timeout;\n    ngx_err_t   err;\n    ngx_int_t   n;\n    ngx_msec_t  timer;\n    ngx_uint_t  live;\n    HANDLE      events[MAXIMUM_WAIT_OBJECTS];\n\n    ngx_sprintf((u_char *) ngx_master_process_event_name,\n                \"ngx_master_%s%Z\", ngx_unique);\n\n    if (ngx_process == NGX_PROCESS_WORKER) {\n        ngx_worker_process_cycle(cycle, ngx_master_process_event_name);\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, \"master started\");\n\n    ngx_console_init(cycle);\n\n    SetEnvironmentVariable(\"ngx_unique\", ngx_unique);\n\n    ngx_master_process_event = CreateEvent(NULL, 1, 0,\n                                           ngx_master_process_event_name);\n    if (ngx_master_process_event == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\",\n                      ngx_master_process_event_name);\n        exit(2);\n    }\n\n    if (ngx_create_signal_events(cycle) != NGX_OK) {\n        exit(2);\n    }\n\n    ngx_sprintf((u_char *) ngx_cache_manager_mutex_name,\n                \"ngx_cache_manager_mutex_%s%Z\", ngx_unique);\n\n    ngx_cache_manager_mutex = CreateMutex(NULL, 0,\n                                          ngx_cache_manager_mutex_name);\n    if (ngx_cache_manager_mutex == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                   \"CreateMutex(\\\"%s\\\") failed\", ngx_cache_manager_mutex_name);\n        exit(2);\n    }\n\n\n    events[0] = ngx_stop_event;\n    events[1] = ngx_quit_event;\n    events[2] = ngx_reopen_event;\n    events[3] = ngx_reload_event;\n\n    ngx_close_listening_sockets(cycle);\n\n    if (ngx_start_worker_processes(cycle, NGX_PROCESS_RESPAWN) == 0) {\n        exit(2);\n    }\n\n    timer = 0;\n    timeout = INFINITE;\n\n    for ( ;; ) {\n\n        nev = 4;\n        for (n = 0; n < ngx_last_process; n++) {\n            if (ngx_processes[n].handle) {\n                events[nev++] = ngx_processes[n].handle;\n            }\n        }\n\n        if (timer) {\n            timeout = timer > ngx_current_msec ? timer - ngx_current_msec : 0;\n        }\n\n        ev = WaitForMultipleObjects(nev, events, 0, timeout);\n\n        err = ngx_errno;\n        ngx_time_update();\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"master WaitForMultipleObjects: %ul\", ev);\n\n        if (ev == WAIT_OBJECT_0) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exiting\");\n\n            if (ResetEvent(ngx_stop_event) == 0) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"ResetEvent(\\\"%s\\\") failed\", ngx_stop_event_name);\n            }\n\n            if (timer == 0) {\n                timer = ngx_current_msec + 5000;\n            }\n\n            ngx_terminate = 1;\n            ngx_quit_worker_processes(cycle, 0);\n\n            continue;\n        }\n\n        if (ev == WAIT_OBJECT_0 + 1) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"shutting down\");\n\n            if (ResetEvent(ngx_quit_event) == 0) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"ResetEvent(\\\"%s\\\") failed\", ngx_quit_event_name);\n            }\n\n            ngx_quit = 1;\n            ngx_quit_worker_processes(cycle, 0);\n\n            continue;\n        }\n\n        if (ev == WAIT_OBJECT_0 + 2) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reopening logs\");\n\n            if (ResetEvent(ngx_reopen_event) == 0) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"ResetEvent(\\\"%s\\\") failed\",\n                              ngx_reopen_event_name);\n            }\n\n            ngx_reopen_files(cycle, -1);\n            ngx_reopen_worker_processes(cycle);\n\n            continue;\n        }\n\n        if (ev == WAIT_OBJECT_0 + 3) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reconfiguring\");\n\n            if (ResetEvent(ngx_reload_event) == 0) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"ResetEvent(\\\"%s\\\") failed\",\n                              ngx_reload_event_name);\n            }\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            ngx_close_listening_sockets(cycle);\n\n            if (ngx_start_worker_processes(cycle, NGX_PROCESS_JUST_RESPAWN)) {\n                ngx_quit_worker_processes(cycle, 1);\n            }\n\n            continue;\n        }\n\n        if (ev > WAIT_OBJECT_0 + 3 && ev < WAIT_OBJECT_0 + nev) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, \"reap worker\");\n\n            live = ngx_reap_worker(cycle, events[ev]);\n\n            if (!live && (ngx_terminate || ngx_quit)) {\n                ngx_master_process_exit(cycle);\n            }\n\n            continue;\n        }\n\n        if (ev == WAIT_TIMEOUT) {\n            ngx_terminate_worker_processes(cycle);\n\n            ngx_master_process_exit(cycle);\n        }\n\n        if (ev == WAIT_FAILED) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"WaitForMultipleObjects() failed\");\n\n            continue;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n            \"WaitForMultipleObjects() returned unexpected value %ul\", ev);\n    }\n}\n\n\nstatic void\nngx_console_init(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->daemon) {\n        if (FreeConsole() == 0) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"FreeConsole() failed\");\n        }\n\n        return;\n    }\n\n    if (SetConsoleCtrlHandler(ngx_console_handler, 1) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"SetConsoleCtrlHandler() failed\");\n    }\n}\n\n\nstatic int __stdcall\nngx_console_handler(u_long type)\n{\n    char  *msg;\n\n    switch (type) {\n\n    case CTRL_C_EVENT:\n        msg = \"Ctrl-C pressed, exiting\";\n        break;\n\n    case CTRL_BREAK_EVENT:\n        msg = \"Ctrl-Break pressed, exiting\";\n        break;\n\n    case CTRL_CLOSE_EVENT:\n        msg = \"console closing, exiting\";\n        break;\n\n    case CTRL_LOGOFF_EVENT:\n        msg = \"user logs off, exiting\";\n        break;\n\n    default:\n        return 0;\n    }\n\n    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, msg);\n\n    if (ngx_stop_event == NULL) {\n        return 1;\n    }\n\n    if (SetEvent(ngx_stop_event) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      \"SetEvent(\\\"%s\\\") failed\", ngx_stop_event_name);\n    }\n\n    return 1;\n}\n\n\nstatic ngx_int_t\nngx_create_signal_events(ngx_cycle_t *cycle)\n{\n    ngx_sprintf((u_char *) ngx_stop_event_name,\n                \"Global\\\\ngx_stop_%s%Z\", ngx_unique);\n\n    ngx_stop_event = CreateEvent(NULL, 1, 0, ngx_stop_event_name);\n    if (ngx_stop_event == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\", ngx_stop_event_name);\n        return NGX_ERROR;\n    }\n\n\n    ngx_sprintf((u_char *) ngx_quit_event_name,\n                \"Global\\\\ngx_quit_%s%Z\", ngx_unique);\n\n    ngx_quit_event = CreateEvent(NULL, 1, 0, ngx_quit_event_name);\n    if (ngx_quit_event == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\", ngx_quit_event_name);\n        return NGX_ERROR;\n    }\n\n\n    ngx_sprintf((u_char *) ngx_reopen_event_name,\n                \"Global\\\\ngx_reopen_%s%Z\", ngx_unique);\n\n    ngx_reopen_event = CreateEvent(NULL, 1, 0, ngx_reopen_event_name);\n    if (ngx_reopen_event == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\", ngx_reopen_event_name);\n        return NGX_ERROR;\n    }\n\n\n    ngx_sprintf((u_char *) ngx_reload_event_name,\n                \"Global\\\\ngx_reload_%s%Z\", ngx_unique);\n\n    ngx_reload_event = CreateEvent(NULL, 1, 0, ngx_reload_event_name);\n    if (ngx_reload_event == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\", ngx_reload_event_name);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t type)\n{\n    ngx_int_t         n;\n    ngx_core_conf_t  *ccf;\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"start worker processes\");\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    for (n = 0; n < ccf->worker_processes; n++) {\n        if (ngx_spawn_process(cycle, \"worker\", type) == NGX_INVALID_PID) {\n            break;\n        }\n    }\n\n    return n;\n}\n\n\nstatic void\nngx_reopen_worker_processes(ngx_cycle_t *cycle)\n{\n    ngx_int_t  n;\n\n    for (n = 0; n < ngx_last_process; n++) {\n\n        if (ngx_processes[n].handle == NULL) {\n            continue;\n        }\n\n        if (SetEvent(ngx_processes[n].reopen) == 0) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"SetEvent(\\\"%s\\\") failed\",\n                          ngx_processes[n].reopen_event);\n        }\n    }\n}\n\n\nstatic void\nngx_quit_worker_processes(ngx_cycle_t *cycle, ngx_uint_t old)\n{\n    ngx_int_t  n;\n\n    for (n = 0; n < ngx_last_process; n++) {\n\n        ngx_log_debug5(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"process: %d %P %p e:%d j:%d\",\n                       n,\n                       ngx_processes[n].pid,\n                       ngx_processes[n].handle,\n                       ngx_processes[n].exiting,\n                       ngx_processes[n].just_spawn);\n\n        if (old && ngx_processes[n].just_spawn) {\n            ngx_processes[n].just_spawn = 0;\n            continue;\n        }\n\n        if (ngx_processes[n].handle == NULL) {\n            continue;\n        }\n\n        if (SetEvent(ngx_processes[n].quit) == 0) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"SetEvent(\\\"%s\\\") failed\",\n                          ngx_processes[n].quit_event);\n        }\n\n        ngx_processes[n].exiting = 1;\n    }\n}\n\n\nstatic void\nngx_terminate_worker_processes(ngx_cycle_t *cycle)\n{\n    ngx_int_t  n;\n\n    for (n = 0; n < ngx_last_process; n++) {\n\n        if (ngx_processes[n].handle == NULL) {\n            continue;\n        }\n\n        if (TerminateProcess(ngx_processes[n].handle, 0) == 0) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"TerminateProcess(\\\"%p\\\") failed\",\n                          ngx_processes[n].handle);\n        }\n\n        ngx_processes[n].exiting = 1;\n\n        ngx_close_handle(ngx_processes[n].reopen);\n        ngx_close_handle(ngx_processes[n].quit);\n        ngx_close_handle(ngx_processes[n].term);\n        ngx_close_handle(ngx_processes[n].handle);\n    }\n}\n\n\nstatic ngx_uint_t\nngx_reap_worker(ngx_cycle_t *cycle, HANDLE h)\n{\n    u_long     code;\n    ngx_int_t  n;\n\n    for (n = 0; n < ngx_last_process; n++) {\n\n        if (ngx_processes[n].handle != h) {\n            continue;\n        }\n\n        if (GetExitCodeProcess(h, &code) == 0) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"GetExitCodeProcess(%P) failed\",\n                          ngx_processes[n].pid);\n        }\n\n        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,\n                      \"%s process %P exited with code %Xl\",\n                      ngx_processes[n].name, ngx_processes[n].pid, code);\n\n        ngx_close_handle(ngx_processes[n].reopen);\n        ngx_close_handle(ngx_processes[n].quit);\n        ngx_close_handle(ngx_processes[n].term);\n        ngx_close_handle(h);\n\n        ngx_processes[n].handle = NULL;\n        ngx_processes[n].term = NULL;\n        ngx_processes[n].quit = NULL;\n        ngx_processes[n].reopen = NULL;\n\n        if (!ngx_processes[n].exiting && !ngx_terminate && !ngx_quit) {\n\n            if (ngx_spawn_process(cycle, ngx_processes[n].name, n)\n                == NGX_INVALID_PID)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"could not respawn %s\", ngx_processes[n].name);\n\n                if (n == ngx_last_process - 1) {\n                    ngx_last_process--;\n                }\n            }\n        }\n\n        goto found;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, \"unknown process handle %p\", h);\n\nfound:\n\n    for (n = 0; n < ngx_last_process; n++) {\n\n        ngx_log_debug5(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"process: %d %P %p e:%d j:%d\",\n                       n,\n                       ngx_processes[n].pid,\n                       ngx_processes[n].handle,\n                       ngx_processes[n].exiting,\n                       ngx_processes[n].just_spawn);\n\n        if (ngx_processes[n].handle) {\n            return 1;\n        }\n    }\n\n    return 0;\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_close_handle(ngx_cache_manager_mutex);\n    ngx_close_handle(ngx_stop_event);\n    ngx_close_handle(ngx_quit_event);\n    ngx_close_handle(ngx_reopen_event);\n    ngx_close_handle(ngx_reload_event);\n    ngx_close_handle(ngx_master_process_event);\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_destroy_pool(cycle->pool);\n\n    exit(0);\n}\n\n\nstatic void\nngx_worker_process_cycle(ngx_cycle_t *cycle, char *mevn)\n{\n    char        wtevn[NGX_PROCESS_SYNC_NAME];\n    char        wqevn[NGX_PROCESS_SYNC_NAME];\n    char        wroevn[NGX_PROCESS_SYNC_NAME];\n    HANDLE      mev, events[3];\n    u_long      nev, ev;\n    ngx_err_t   err;\n    ngx_tid_t   wtid, cmtid, cltid;\n    ngx_log_t  *log;\n\n    log = cycle->log;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, \"worker started\");\n\n    ngx_sprintf((u_char *) wtevn, \"ngx_worker_term_%P%Z\", ngx_pid);\n    events[0] = CreateEvent(NULL, 1, 0, wtevn);\n    if (events[0] == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\", wtevn);\n        goto failed;\n    }\n\n    ngx_sprintf((u_char *) wqevn, \"ngx_worker_quit_%P%Z\", ngx_pid);\n    events[1] = CreateEvent(NULL, 1, 0, wqevn);\n    if (events[1] == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\", wqevn);\n        goto failed;\n    }\n\n    ngx_sprintf((u_char *) wroevn, \"ngx_worker_reopen_%P%Z\", ngx_pid);\n    events[2] = CreateEvent(NULL, 1, 0, wroevn);\n    if (events[2] == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"CreateEvent(\\\"%s\\\") failed\", wroevn);\n        goto failed;\n    }\n\n    mev = OpenEvent(EVENT_MODIFY_STATE, 0, mevn);\n    if (mev == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"OpenEvent(\\\"%s\\\") failed\", mevn);\n        goto failed;\n    }\n\n    if (SetEvent(mev) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"SetEvent(\\\"%s\\\") failed\", mevn);\n        goto failed;\n    }\n\n\n    ngx_sprintf((u_char *) ngx_cache_manager_mutex_name,\n                \"ngx_cache_manager_mutex_%s%Z\", ngx_unique);\n\n    ngx_cache_manager_mutex = OpenMutex(SYNCHRONIZE, 0,\n                                        ngx_cache_manager_mutex_name);\n    if (ngx_cache_manager_mutex == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"OpenMutex(\\\"%s\\\") failed\", ngx_cache_manager_mutex_name);\n        goto failed;\n    }\n\n    ngx_cache_manager_event = CreateEvent(NULL, 1, 0, NULL);\n    if (ngx_cache_manager_event == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CreateEvent(\\\"ngx_cache_manager_event\\\") failed\");\n        goto failed;\n    }\n\n\n    if (ngx_create_thread(&wtid, ngx_worker_thread, NULL, log) != 0) {\n        goto failed;\n    }\n\n    if (ngx_create_thread(&cmtid, ngx_cache_manager_thread, NULL, log) != 0) {\n        goto failed;\n    }\n\n    if (ngx_create_thread(&cltid, ngx_cache_loader_thread, NULL, log) != 0) {\n        goto failed;\n    }\n\n    for ( ;; ) {\n        ev = WaitForMultipleObjects(3, events, 0, INFINITE);\n\n        err = ngx_errno;\n        ngx_time_update();\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,\n                       \"worker WaitForMultipleObjects: %ul\", ev);\n\n        if (ev == WAIT_OBJECT_0) {\n            ngx_terminate = 1;\n            ngx_log_error(NGX_LOG_NOTICE, log, 0, \"exiting\");\n\n            if (ResetEvent(events[0]) == 0) {\n                ngx_log_error(NGX_LOG_ALERT, log, 0,\n                              \"ResetEvent(\\\"%s\\\") failed\", wtevn);\n            }\n\n            break;\n        }\n\n        if (ev == WAIT_OBJECT_0 + 1) {\n            ngx_quit = 1;\n            ngx_log_error(NGX_LOG_NOTICE, log, 0, \"gracefully shutting down\");\n            break;\n        }\n\n        if (ev == WAIT_OBJECT_0 + 2) {\n            ngx_reopen = 1;\n            ngx_log_error(NGX_LOG_NOTICE, log, 0, \"reopening logs\");\n\n            if (ResetEvent(events[2]) == 0) {\n                ngx_log_error(NGX_LOG_ALERT, log, 0,\n                              \"ResetEvent(\\\"%s\\\") failed\", wroevn);\n            }\n\n            continue;\n        }\n\n        if (ev == WAIT_FAILED) {\n            ngx_log_error(NGX_LOG_ALERT, log, err,\n                          \"WaitForMultipleObjects() failed\");\n\n            goto failed;\n        }\n    }\n\n    /* wait threads */\n\n    if (SetEvent(ngx_cache_manager_event) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"SetEvent(\\\"ngx_cache_manager_event\\\") failed\");\n    }\n\n    events[1] = wtid;\n    events[2] = cmtid;\n\n    nev = 3;\n\n    for ( ;; ) {\n        ev = WaitForMultipleObjects(nev, events, 0, INFINITE);\n\n        err = ngx_errno;\n        ngx_time_update();\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,\n                       \"worker exit WaitForMultipleObjects: %ul\", ev);\n\n        if (ev == WAIT_OBJECT_0) {\n            break;\n        }\n\n        if (ev == WAIT_OBJECT_0 + 1) {\n            if (nev == 2) {\n                break;\n            }\n\n            events[1] = events[2];\n            nev = 2;\n            continue;\n        }\n\n        if (ev == WAIT_OBJECT_0 + 2) {\n            nev = 2;\n            continue;\n        }\n\n        if (ev == WAIT_FAILED) {\n            ngx_log_error(NGX_LOG_ALERT, log, err,\n                          \"WaitForMultipleObjects() failed\");\n            break;\n        }\n    }\n\n    ngx_close_handle(ngx_cache_manager_event);\n    ngx_close_handle(events[0]);\n    ngx_close_handle(events[1]);\n    ngx_close_handle(events[2]);\n    ngx_close_handle(mev);\n\n    ngx_worker_process_exit(cycle);\n\nfailed:\n\n    exit(2);\n}\n\n\nstatic ngx_thread_value_t __stdcall\nngx_worker_thread(void *data)\n{\n    ngx_int_t     n;\n    ngx_time_t   *tp;\n    ngx_cycle_t  *cycle;\n\n    tp = ngx_timeofday();\n    srand((ngx_pid << 16) ^ (unsigned) tp->sec ^ tp->msec);\n\n    cycle = (ngx_cycle_t *) ngx_cycle;\n\n    for (n = 0; cycle->modules[n]; n++) {\n        if (cycle->modules[n]->init_process) {\n            if (cycle->modules[n]->init_process(cycle) == NGX_ERROR) {\n                /* fatal */\n                exit(2);\n            }\n        }\n    }\n\n    while (!ngx_quit) {\n\n        if (ngx_exiting) {\n            if (ngx_event_no_timers_left() == NGX_OK) {\n                break;\n            }\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, \"worker cycle\");\n\n        ngx_process_events_and_timers(cycle);\n\n        if (ngx_terminate) {\n            return 0;\n        }\n\n        if (ngx_quit) {\n            ngx_quit = 0;\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            }\n        }\n\n        if (ngx_reopen) {\n            ngx_reopen = 0;\n            ngx_reopen_files(cycle, -1);\n        }\n    }\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exiting\");\n\n    return 0;\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    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_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 != (ngx_socket_t) -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            }\n        }\n    }\n\n    ngx_destroy_pool(cycle->pool);\n\n    exit(0);\n}\n\n\nstatic ngx_thread_value_t __stdcall\nngx_cache_manager_thread(void *data)\n{\n    u_long        ev;\n    HANDLE        events[2];\n    ngx_err_t     err;\n    ngx_cycle_t  *cycle;\n\n    cycle = (ngx_cycle_t *) ngx_cycle;\n\n    events[0] = ngx_cache_manager_event;\n    events[1] = ngx_cache_manager_mutex;\n\n    for ( ;; ) {\n        ev = WaitForMultipleObjects(2, events, 0, INFINITE);\n\n        err = ngx_errno;\n        ngx_time_update();\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"cache manager WaitForMultipleObjects: %ul\", ev);\n\n        if (ev == WAIT_FAILED) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"WaitForMultipleObjects() failed\");\n        }\n\n        /*\n         * ev == WAIT_OBJECT_0\n         * ev == WAIT_OBJECT_0 + 1\n         * ev == WAIT_ABANDONED_0 + 1\n         */\n\n        if (ngx_terminate || ngx_quit || ngx_exiting) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exiting\");\n            return 0;\n        }\n\n        break;\n    }\n\n    for ( ;; ) {\n\n        if (ngx_terminate || ngx_quit || ngx_exiting) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exiting\");\n            break;\n        }\n\n        ngx_cache_manager_process_handler();\n    }\n\n    if (ReleaseMutex(ngx_cache_manager_mutex) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"ReleaseMutex() failed\");\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_cache_manager_process_handler(void)\n{\n    u_long        ev;\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    ev = WaitForSingleObject(ngx_cache_manager_event, (u_long) next);\n\n    if (ev != WAIT_TIMEOUT) {\n\n        ngx_time_update();\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                       \"cache manager WaitForSingleObject: %ul\", ev);\n    }\n}\n\n\nstatic ngx_thread_value_t __stdcall\nngx_cache_loader_thread(void *data)\n{\n    ngx_uint_t     i;\n    ngx_path_t   **path;\n    ngx_cycle_t   *cycle;\n\n    ngx_msleep(60000);\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 || ngx_exiting) {\n            break;\n        }\n\n        if (path[i]->loader) {\n            path[i]->loader(path[i]->data);\n            ngx_time_update();\n        }\n    }\n\n    return 0;\n}\n\n\nvoid\nngx_single_process_cycle(ngx_cycle_t *cycle)\n{\n    ngx_tid_t  tid;\n\n    ngx_console_init(cycle);\n\n    if (ngx_create_signal_events(cycle) != NGX_OK) {\n        exit(2);\n    }\n\n    if (ngx_create_thread(&tid, ngx_worker_thread, NULL, cycle->log) != 0) {\n        /* fatal */\n        exit(2);\n    }\n\n    /* STUB */\n    WaitForSingleObject(ngx_stop_event, INFINITE);\n}\n\n\nngx_int_t\nngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_pid_t pid)\n{\n    HANDLE     ev;\n    ngx_int_t  rc;\n    char       evn[NGX_PROCESS_SYNC_NAME];\n\n    ngx_sprintf((u_char *) evn, \"Global\\\\ngx_%s_%P%Z\", sig, pid);\n\n    ev = OpenEvent(EVENT_MODIFY_STATE, 0, evn);\n    if (ev == NULL) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      \"OpenEvent(\\\"%s\\\") failed\", evn);\n        return 1;\n    }\n\n    if (SetEvent(ev) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"SetEvent(\\\"%s\\\") failed\", evn);\n        rc = 1;\n\n    } else {\n        rc = 0;\n    }\n\n    ngx_close_handle(ev);\n\n    return rc;\n}\n\n\nvoid\nngx_close_handle(HANDLE h)\n{\n    if (CloseHandle(h) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,\n                      \"CloseHandle(%p) failed\", h);\n    }\n}\n"
  },
  {
    "path": "src/os/win32/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_PROCESS_SINGLE     0\n#define NGX_PROCESS_MASTER     1\n#define NGX_PROCESS_SIGNALLER  2\n#define NGX_PROCESS_WORKER     3\n\n\nvoid ngx_master_process_cycle(ngx_cycle_t *cycle);\nvoid ngx_single_process_cycle(ngx_cycle_t *cycle);\nvoid ngx_close_handle(HANDLE h);\n\n\nextern ngx_uint_t      ngx_process;\nextern ngx_uint_t      ngx_worker;\nextern ngx_pid_t       ngx_pid;\nextern ngx_uint_t      ngx_exiting;\n\nextern sig_atomic_t    ngx_quit;\nextern sig_atomic_t    ngx_terminate;\nextern sig_atomic_t    ngx_reopen;\n\nextern ngx_uint_t      ngx_inherited;\nextern ngx_pid_t       ngx_new_binary;\n\n\nextern HANDLE          ngx_master_process_event;\nextern char            ngx_master_process_event_name[];\n\n\n#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/ngx_service.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n\n#define NGX_SERVICE_CONTROL_SHUTDOWN   128\n#define NGX_SERVICE_CONTROL_REOPEN     129\n\n\nSERVICE_TABLE_ENTRY st[] = {\n    { \"nginx\", service_main },\n    { NULL, NULL }\n};\n\n\nngx_int_t\nngx_service(ngx_log_t *log)\n{\n    /* primary thread */\n\n    /* StartServiceCtrlDispatcher() should be called within 30 seconds */\n\n    if (StartServiceCtrlDispatcher(st) == 0) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"StartServiceCtrlDispatcher() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nservice_main(u_int argc, char **argv)\n{\n    SERVICE_STATUS         status;\n    SERVICE_STATUS_HANDLE  service;\n\n    /* thread spawned by SCM */\n\n    service = RegisterServiceCtrlHandlerEx(\"nginx\", service_handler, ctx);\n    if (service == INVALID_HANDLE_VALUE) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"RegisterServiceCtrlHandlerEx() failed\");\n        return;\n    }\n\n    status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;\n    status.dwCurrentState = SERVICE_START_PENDING;\n    status.dwControlsAccepted = SERVICE_ACCEPT_STOP\n                                |SERVICE_ACCEPT_PARAMCHANGE;\n    status.dwWin32ExitCode = NO_ERROR;\n    status.dwServiceSpecificExitCode = 0;\n    status.dwCheckPoint = 1;\n    status.dwWaitHint = 2000;\n\n    /* SetServiceStatus() should be called within 80 seconds */\n\n    if (SetServiceStatus(service, &status) == 0) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"SetServiceStatus() failed\");\n        return;\n    }\n\n    /* init */\n\n    status.dwCurrentState = SERVICE_RUNNING;\n    status.dwCheckPoint = 0;\n    status.dwWaitHint = 0;\n\n    if (SetServiceStatus(service, &status) == 0) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"SetServiceStatus() failed\");\n        return;\n    }\n\n    /* call master or worker loop */\n\n    /*\n     * master should use event notification and look status\n     * single should use iocp to get notifications from service handler\n     */\n\n}\n\n\nu_int\nservice_handler(u_int control, u_int type, void *data, void *ctx)\n{\n    /* primary thread */\n\n    switch (control) {\n\n    case SERVICE_CONTROL_INTERROGATE:\n        status = NGX_IOCP_INTERROGATE;\n        break;\n\n    case SERVICE_CONTROL_STOP:\n        status = NGX_IOCP_STOP;\n        break;\n\n    case SERVICE_CONTROL_PARAMCHANGE:\n        status = NGX_IOCP_RECONFIGURE;\n        break;\n\n    case NGX_SERVICE_CONTROL_SHUTDOWN:\n        status = NGX_IOCP_REOPEN;\n        break;\n\n    case NGX_SERVICE_CONTROL_REOPEN:\n        status = NGX_IOCP_REOPEN;\n        break;\n\n    default:\n        return ERROR_CALL_NOT_IMPLEMENTED;\n    }\n\n    if (ngx_single) {\n        if (PostQueuedCompletionStatus(iocp, ... status, ...) == 0) {\n            err = ngx_errno;\n            ngx_log_error(NGX_LOG_ALERT, log, err,\n                          \"PostQueuedCompletionStatus() failed\");\n            return err;\n        }\n\n    } else {\n        Event\n    }\n\n    return NO_ERROR;\n}\n"
  },
  {
    "path": "src/os/win32/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/*\n * Base addresses selected by system for shared memory mappings are likely\n * to be different on Windows Vista and later versions due to address space\n * layout randomization.  This is however incompatible with storing absolute\n * addresses within the shared memory.\n *\n * To make it possible to store absolute addresses we create mappings\n * at the same address in all processes by starting mappings at predefined\n * addresses.  The addresses were selected somewhat randomly in order to\n * minimize the probability that some other library doing something similar\n * conflicts with us.  The addresses are from the following typically free\n * blocks:\n *\n * - 0x10000000 .. 0x70000000 (about 1.5 GB in total) on 32-bit platforms\n * - 0x000000007fff0000 .. 0x000007f68e8b0000 (about 8 TB) on 64-bit platforms\n *\n * Additionally, we allow to change the mapping address once it was detected\n * to be different from one originally used.  This is needed to support\n * reconfiguration.\n */\n\n\n#ifdef _WIN64\n#define NGX_SHMEM_BASE  0x0000047047e00000\n#else\n#define NGX_SHMEM_BASE  0x2efe0000\n#endif\n\n\nngx_uint_t  ngx_allocation_granularity;\n\n\nngx_int_t\nngx_shm_alloc(ngx_shm_t *shm)\n{\n    u_char         *name;\n    uint64_t        size;\n    static u_char  *base = (u_char *) NGX_SHMEM_BASE;\n\n    name = ngx_alloc(shm->name.len + 2 + NGX_INT32_LEN, shm->log);\n    if (name == NULL) {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_sprintf(name, \"%V_%s%Z\", &shm->name, ngx_unique);\n\n    ngx_set_errno(0);\n\n    size = shm->size;\n\n    shm->handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,\n                                    (u_long) (size >> 32),\n                                    (u_long) (size & 0xffffffff),\n                                    (char *) name);\n\n    if (shm->handle == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"CreateFileMapping(%uz, %s) failed\",\n                      shm->size, name);\n        ngx_free(name);\n\n        return NGX_ERROR;\n    }\n\n    ngx_free(name);\n\n    if (ngx_errno == ERROR_ALREADY_EXISTS) {\n        shm->exists = 1;\n    }\n\n    shm->addr = MapViewOfFileEx(shm->handle, FILE_MAP_WRITE, 0, 0, 0, base);\n\n    if (shm->addr != NULL) {\n        base += ngx_align(size, ngx_allocation_granularity);\n        return NGX_OK;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm->log, ngx_errno,\n                   \"MapViewOfFileEx(%uz, %p) of file mapping \\\"%V\\\" failed, \"\n                   \"retry without a base address\",\n                   shm->size, base, &shm->name);\n\n    /*\n     * Order of shared memory zones may be different in the master process\n     * and worker processes after reconfiguration.  As a result, the above\n     * may fail due to a conflict with a previously created mapping remapped\n     * to a different address.  Additionally, there may be a conflict with\n     * some other uses of the memory.  In this case we retry without a base\n     * address to let the system assign the address itself.\n     */\n\n    shm->addr = MapViewOfFile(shm->handle, FILE_MAP_WRITE, 0, 0, 0);\n\n    if (shm->addr != NULL) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                  \"MapViewOfFile(%uz) of file mapping \\\"%V\\\" failed\",\n                  shm->size, &shm->name);\n\n    if (CloseHandle(shm->handle) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"CloseHandle() of file mapping \\\"%V\\\" failed\",\n                      &shm->name);\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_shm_remap(ngx_shm_t *shm, u_char *addr)\n{\n    if (UnmapViewOfFile(shm->addr) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"UnmapViewOfFile(%p) of file mapping \\\"%V\\\" failed\",\n                      shm->addr, &shm->name);\n        return NGX_ERROR;\n    }\n\n    shm->addr = MapViewOfFileEx(shm->handle, FILE_MAP_WRITE, 0, 0, 0, addr);\n\n    if (shm->addr != NULL) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                  \"MapViewOfFileEx(%uz, %p) of file mapping \\\"%V\\\" failed\",\n                  shm->size, addr, &shm->name);\n\n    return NGX_ERROR;\n}\n\n\nvoid\nngx_shm_free(ngx_shm_t *shm)\n{\n    if (UnmapViewOfFile(shm->addr) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"UnmapViewOfFile(%p) of file mapping \\\"%V\\\" failed\",\n                      shm->addr, &shm->name);\n    }\n\n    if (CloseHandle(shm->handle) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"CloseHandle() of file mapping \\\"%V\\\" failed\",\n                      &shm->name);\n    }\n}\n"
  },
  {
    "path": "src/os/win32/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    HANDLE       handle;\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);\nngx_int_t ngx_shm_remap(ngx_shm_t *shm, u_char *addr);\nvoid ngx_shm_free(ngx_shm_t *shm);\n\nextern ngx_uint_t  ngx_allocation_granularity;\n\n\n#endif /* _NGX_SHMEM_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/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\nint\nngx_nonblocking(ngx_socket_t s)\n{\n    unsigned long  nb = 1;\n\n    return ioctlsocket(s, FIONBIO, &nb);\n}\n\n\nint\nngx_blocking(ngx_socket_t s)\n{\n    unsigned long  nb = 0;\n\n    return ioctlsocket(s, FIONBIO, &nb);\n}\n\n\nint\nngx_socket_nread(ngx_socket_t s, int *n)\n{\n    unsigned long  nread;\n\n    if (ioctlsocket(s, FIONREAD, &nread) == -1) {\n        return -1;\n    }\n\n    *n = nread;\n\n    return 0;\n}\n\n\nint\nngx_tcp_push(ngx_socket_t s)\n{\n    return 0;\n}\n"
  },
  {
    "path": "src/os/win32/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#include <ngx_core.h>\n\n\n#define NGX_WRITE_SHUTDOWN SD_SEND\n\n\ntypedef SOCKET  ngx_socket_t;\ntypedef int     socklen_t;\n\n\n#define ngx_socket(af, type, proto)                                          \\\n    WSASocketW(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED)\n\n#define ngx_socket_n        \"WSASocketW()\"\n\nint ngx_nonblocking(ngx_socket_t s);\nint ngx_blocking(ngx_socket_t s);\n\n#define ngx_nonblocking_n   \"ioctlsocket(FIONBIO)\"\n#define ngx_blocking_n      \"ioctlsocket(!FIONBIO)\"\n\nint ngx_socket_nread(ngx_socket_t s, int *n);\n#define ngx_socket_nread_n  \"ioctlsocket(FIONREAD)\"\n\n#define ngx_shutdown_socket    shutdown\n#define ngx_shutdown_socket_n  \"shutdown()\"\n\n#define ngx_close_socket    closesocket\n#define ngx_close_socket_n  \"closesocket()\"\n\n\n#ifndef WSAID_ACCEPTEX\n\ntypedef BOOL (PASCAL FAR * LPFN_ACCEPTEX)(\n    IN SOCKET sListenSocket,\n    IN SOCKET sAcceptSocket,\n    IN PVOID lpOutputBuffer,\n    IN DWORD dwReceiveDataLength,\n    IN DWORD dwLocalAddressLength,\n    IN DWORD dwRemoteAddressLength,\n    OUT LPDWORD lpdwBytesReceived,\n    IN LPOVERLAPPED lpOverlapped\n    );\n\n#define WSAID_ACCEPTEX                                                       \\\n    {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}\n\n#endif\n\n\n#ifndef WSAID_GETACCEPTEXSOCKADDRS\n\ntypedef VOID (PASCAL FAR * LPFN_GETACCEPTEXSOCKADDRS)(\n    IN PVOID lpOutputBuffer,\n    IN DWORD dwReceiveDataLength,\n    IN DWORD dwLocalAddressLength,\n    IN DWORD dwRemoteAddressLength,\n    OUT struct sockaddr **LocalSockaddr,\n    OUT LPINT LocalSockaddrLength,\n    OUT struct sockaddr **RemoteSockaddr,\n    OUT LPINT RemoteSockaddrLength\n    );\n\n#define WSAID_GETACCEPTEXSOCKADDRS                                           \\\n        {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}\n\n#endif\n\n\n#ifndef WSAID_TRANSMITFILE\n\n#ifndef TF_DISCONNECT\n\n#define TF_DISCONNECT           1\n#define TF_REUSE_SOCKET         2\n#define TF_WRITE_BEHIND         4\n#define TF_USE_DEFAULT_WORKER   0\n#define TF_USE_SYSTEM_THREAD    16\n#define TF_USE_KERNEL_APC       32\n\ntypedef struct _TRANSMIT_FILE_BUFFERS {\n    LPVOID Head;\n    DWORD HeadLength;\n    LPVOID Tail;\n    DWORD TailLength;\n} TRANSMIT_FILE_BUFFERS, *PTRANSMIT_FILE_BUFFERS, FAR *LPTRANSMIT_FILE_BUFFERS;\n\n#endif\n\ntypedef BOOL (PASCAL FAR * LPFN_TRANSMITFILE)(\n    IN SOCKET hSocket,\n    IN HANDLE hFile,\n    IN DWORD nNumberOfBytesToWrite,\n    IN DWORD nNumberOfBytesPerSend,\n    IN LPOVERLAPPED lpOverlapped,\n    IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers,\n    IN DWORD dwReserved\n    );\n\n#define WSAID_TRANSMITFILE                                                   \\\n    {0xb5367df0,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}\n\n#endif\n\n\n#ifndef WSAID_TRANSMITPACKETS\n\n/* OpenWatcom has a swapped TP_ELEMENT_FILE and TP_ELEMENT_MEMORY definition */\n\n#ifndef TP_ELEMENT_FILE\n\n#ifdef _MSC_VER\n#pragma warning(disable:4201) /* Nonstandard extension, nameless struct/union */\n#endif\n\ntypedef struct _TRANSMIT_PACKETS_ELEMENT {\n    ULONG dwElFlags;\n#define TP_ELEMENT_MEMORY   1\n#define TP_ELEMENT_FILE     2\n#define TP_ELEMENT_EOP      4\n    ULONG cLength;\n    union {\n        struct {\n            LARGE_INTEGER nFileOffset;\n            HANDLE        hFile;\n        };\n        PVOID             pBuffer;\n    };\n} TRANSMIT_PACKETS_ELEMENT, *PTRANSMIT_PACKETS_ELEMENT,\n    FAR *LPTRANSMIT_PACKETS_ELEMENT;\n\n#ifdef _MSC_VER\n#pragma warning(default:4201)\n#endif\n\n#endif\n\ntypedef BOOL (PASCAL FAR * LPFN_TRANSMITPACKETS) (\n    SOCKET hSocket,\n    TRANSMIT_PACKETS_ELEMENT *lpPacketArray,\n    DWORD nElementCount,\n    DWORD nSendSize,\n    LPOVERLAPPED lpOverlapped,\n    DWORD dwFlags\n    );\n\n#define WSAID_TRANSMITPACKETS                                                \\\n    {0xd9689da0,0x1f90,0x11d3,{0x99,0x71,0x00,0xc0,0x4f,0x68,0xc8,0x76}}\n\n#endif\n\n\n#ifndef WSAID_CONNECTEX\n\ntypedef BOOL (PASCAL FAR * LPFN_CONNECTEX) (\n    IN SOCKET s,\n    IN const struct sockaddr FAR *name,\n    IN int namelen,\n    IN PVOID lpSendBuffer OPTIONAL,\n    IN DWORD dwSendDataLength,\n    OUT LPDWORD lpdwBytesSent,\n    IN LPOVERLAPPED lpOverlapped\n    );\n\n#define WSAID_CONNECTEX \\\n    {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}}\n\n#endif\n\n\n#ifndef WSAID_DISCONNECTEX\n\ntypedef BOOL (PASCAL FAR * LPFN_DISCONNECTEX) (\n    IN SOCKET s,\n    IN LPOVERLAPPED lpOverlapped,\n    IN DWORD  dwFlags,\n    IN DWORD  dwReserved\n    );\n\n#define WSAID_DISCONNECTEX                                                   \\\n    {0x7fda2e11,0x8630,0x436f,{0xa0,0x31,0xf5,0x36,0xa6,0xee,0xc1,0x57}}\n\n#endif\n\n\nextern LPFN_ACCEPTEX              ngx_acceptex;\nextern LPFN_GETACCEPTEXSOCKADDRS  ngx_getacceptexsockaddrs;\nextern LPFN_TRANSMITFILE          ngx_transmitfile;\nextern LPFN_TRANSMITPACKETS       ngx_transmitpackets;\nextern LPFN_CONNECTEX             ngx_connectex;\nextern LPFN_DISCONNECTEX          ngx_disconnectex;\n\n\n#if (NGX_HAVE_POLL && !defined POLLIN)\n\n/*\n * WSAPoll() is only available if _WIN32_WINNT >= 0x0600.\n * If it is not available during compilation, we try to\n * load it dynamically at runtime.\n */\n\n#define NGX_LOAD_WSAPOLL 1\n\n#define POLLRDNORM  0x0100\n#define POLLRDBAND  0x0200\n#define POLLIN      (POLLRDNORM | POLLRDBAND)\n#define POLLPRI     0x0400\n\n#define POLLWRNORM  0x0010\n#define POLLOUT     (POLLWRNORM)\n#define POLLWRBAND  0x0020\n\n#define POLLERR     0x0001\n#define POLLHUP     0x0002\n#define POLLNVAL    0x0004\n\ntypedef struct pollfd {\n\n    SOCKET  fd;\n    SHORT   events;\n    SHORT   revents;\n\n} WSAPOLLFD, *PWSAPOLLFD, FAR *LPWSAPOLLFD;\n\ntypedef int (WSAAPI *ngx_wsapoll_pt)(\n    LPWSAPOLLFD fdArray,\n    ULONG fds,\n    INT timeout\n    );\n\nextern ngx_wsapoll_pt             WSAPoll;\nextern ngx_uint_t                 ngx_have_wsapoll;\n\n#endif\n\n\nint ngx_tcp_push(ngx_socket_t s);\n#define ngx_tcp_push_n            \"tcp_push()\"\n\n\n#endif /* _NGX_SOCKET_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/ngx_stat.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\nint ngx_file_type(char *file, ngx_file_info_t *sb)\n{\n    sb->dwFileAttributes = GetFileAttributes(file);\n\n    if (sb->dwFileAttributes == INVALID_FILE_ATTRIBUTES) {\n        return -1;\n    }\n\n    return 0;\n}\n\n/*\nint ngx_stat(char *file, ngx_stat_t *sb)\n{\n    *sb = GetFileAttributes(file);\n\n    if (*sb == INVALID_FILE_ATTRIBUTES) {\n        return -1;\n    }\n\n    return 0;\n}\n*/\n"
  },
  {
    "path": "src/os/win32/ngx_thread.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_err_t\nngx_create_thread(ngx_tid_t *tid,\n    ngx_thread_value_t (__stdcall *func)(void *arg), void *arg, ngx_log_t *log)\n{\n    u_long     id;\n    ngx_err_t  err;\n\n    *tid = CreateThread(NULL, 0, func, arg, 0, &id);\n\n    if (*tid != NULL) {\n        ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                      \"create thread \" NGX_TID_T_FMT, id);\n        return 0;\n    }\n\n    err = ngx_errno;\n    ngx_log_error(NGX_LOG_ALERT, log, err, \"CreateThread() failed\");\n    return err;\n}\n"
  },
  {
    "path": "src/os/win32/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\ntypedef HANDLE  ngx_tid_t;\ntypedef DWORD   ngx_thread_value_t;\n\n\nngx_err_t ngx_create_thread(ngx_tid_t *tid,\n    ngx_thread_value_t (__stdcall *func)(void *arg), void *arg, ngx_log_t *log);\n\n#define ngx_log_tid                 GetCurrentThreadId()\n#define NGX_TID_T_FMT               \"%ud\"\n\n\n#endif /* _NGX_THREAD_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/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\nvoid\nngx_gettimeofday(struct timeval *tp)\n{\n    uint64_t  intervals;\n    FILETIME  ft;\n\n    GetSystemTimeAsFileTime(&ft);\n\n    /*\n     * A file time is a 64-bit value that represents the number\n     * of 100-nanosecond intervals that have elapsed since\n     * January 1, 1601 12:00 A.M. UTC.\n     *\n     * Between January 1, 1970 (Epoch) and January 1, 1601 there were\n     * 134774 days,\n     * 11644473600 seconds or\n     * 11644473600,000,000,0 100-nanosecond intervals.\n     *\n     * See also MSKB Q167296.\n     */\n\n    intervals = ((uint64_t) ft.dwHighDateTime << 32) | ft.dwLowDateTime;\n    intervals -= 116444736000000000;\n\n    tp->tv_sec = (long) (intervals / 10000000);\n    tp->tv_usec = (long) ((intervals % 10000000) / 10);\n}\n\n\nvoid\nngx_libc_localtime(time_t s, struct tm *tm)\n{\n    struct tm  *t;\n\n    t = localtime(&s);\n    *tm = *t;\n}\n\n\nvoid\nngx_libc_gmtime(time_t s, struct tm *tm)\n{\n    struct tm  *t;\n\n    t = gmtime(&s);\n    *tm = *t;\n}\n\n\nngx_int_t\nngx_gettimezone(void)\n{\n    u_long                 n;\n    TIME_ZONE_INFORMATION  tz;\n\n    n = GetTimeZoneInformation(&tz);\n\n    switch (n) {\n\n    case TIME_ZONE_ID_UNKNOWN:\n        return -tz.Bias;\n\n    case TIME_ZONE_ID_STANDARD:\n        return -(tz.Bias + tz.StandardBias);\n\n    case TIME_ZONE_ID_DAYLIGHT:\n        return -(tz.Bias + tz.DaylightBias);\n\n    default: /* TIME_ZONE_ID_INVALID */\n        return 0;\n    }\n}\n"
  },
  {
    "path": "src/os/win32/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;\ntypedef ngx_rbtree_key_int_t  ngx_msec_int_t;\n\ntypedef SYSTEMTIME            ngx_tm_t;\ntypedef FILETIME              ngx_mtime_t;\n\n#define ngx_tm_sec            wSecond\n#define ngx_tm_min            wMinute\n#define ngx_tm_hour           wHour\n#define ngx_tm_mday           wDay\n#define ngx_tm_mon            wMonth\n#define ngx_tm_year           wYear\n#define ngx_tm_wday           wDayOfWeek\n\n#define ngx_tm_sec_t          u_short\n#define ngx_tm_min_t          u_short\n#define ngx_tm_hour_t         u_short\n#define ngx_tm_mday_t         u_short\n#define ngx_tm_mon_t          u_short\n#define ngx_tm_year_t         u_short\n#define ngx_tm_wday_t         u_short\n\n\n#define ngx_msleep            Sleep\n\n#define NGX_HAVE_GETTIMEZONE  1\n\n#define  ngx_timezone_update()\n\nngx_int_t ngx_gettimezone(void);\nvoid ngx_libc_localtime(time_t s, struct tm *tm);\nvoid ngx_libc_gmtime(time_t s, struct tm *tm);\nvoid ngx_gettimeofday(struct timeval *tp);\n\n\n#endif /* _NGX_TIME_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/ngx_udp_wsarecv.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_wsarecv(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int           rc;\n    u_long        bytes, flags;\n    WSABUF        wsabuf[1];\n    ngx_err_t     err;\n    ngx_event_t  *rev;\n\n    wsabuf[0].buf = (char *) buf;\n    wsabuf[0].len = size;\n    flags = 0;\n    bytes = 0;\n\n    rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, NULL, NULL);\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"WSARecv: fd:%d rc:%d %ul of %z\", c->fd, rc, bytes, size);\n\n    rev = c->read;\n\n    if (rc == -1) {\n        rev->ready = 0;\n        err = ngx_socket_errno;\n\n        if (err == WSAEWOULDBLOCK) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"WSARecv() not ready\");\n            return NGX_AGAIN;\n        }\n\n        rev->error = 1;\n        ngx_connection_error(c, err, \"WSARecv() failed\");\n\n        return NGX_ERROR;\n    }\n\n    return bytes;\n}\n\n\nssize_t\nngx_udp_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int               rc;\n    u_long            bytes, flags;\n    WSABUF            wsabuf[1];\n    ngx_err_t         err;\n    ngx_event_t      *rev;\n    LPWSAOVERLAPPED   ovlp;\n\n    rev = c->read;\n\n    if (!rev->ready) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"second wsa 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\n    if (rev->complete) {\n        rev->complete = 0;\n\n        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n            if (rev->ovlp.error) {\n                ngx_connection_error(c, rev->ovlp.error, \"WSARecv() failed\");\n                return NGX_ERROR;\n            }\n\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"WSARecv ovlp: fd:%d %ul of %z\",\n                           c->fd, rev->available, size);\n\n            return rev->available;\n        }\n\n        if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &rev->ovlp,\n                                   &bytes, 0, NULL)\n            == 0)\n        {\n            ngx_connection_error(c, ngx_socket_errno,\n                               \"WSARecv() or WSAGetOverlappedResult() failed\");\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"WSARecv: fd:%d %ul of %z\", c->fd, bytes, size);\n\n        return bytes;\n    }\n\n    ovlp = (LPWSAOVERLAPPED) &rev->ovlp;\n    ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));\n    wsabuf[0].buf = (char *) buf;\n    wsabuf[0].len = size;\n    flags = 0;\n    bytes = 0;\n\n    rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, ovlp, NULL);\n\n    rev->complete = 0;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"WSARecv ovlp: fd:%d rc:%d %ul of %z\",\n                   c->fd, rc, bytes, size);\n\n    if (rc == -1) {\n        err = ngx_socket_errno;\n        if (err == WSA_IO_PENDING) {\n            rev->active = 1;\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"WSARecv() posted\");\n            return NGX_AGAIN;\n        }\n\n        rev->error = 1;\n        ngx_connection_error(c, err, \"WSARecv() failed\");\n        return NGX_ERROR;\n    }\n\n    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n\n        /*\n         * if a socket was bound with I/O completion port\n         * then GetQueuedCompletionStatus() would anyway return its status\n         * despite that WSARecv() was already complete\n         */\n\n        rev->active = 1;\n        return NGX_AGAIN;\n    }\n\n    rev->active = 0;\n\n    return bytes;\n}\n"
  },
  {
    "path": "src/os/win32/ngx_user.c",
    "content": "/*\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\nngx_int_t\nngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    /* STUB: a plain text password */\n\n    *encrypted = key;\n\n    return NGX_OK;\n}\n\n#endif /* NGX_CRYPT */\n"
  },
  {
    "path": "src/os/win32/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\n/* STUB */\n#define ngx_uid_t  ngx_int_t\n#define ngx_gid_t  ngx_int_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/win32/ngx_win32_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_WIN32_CONFIG_H_INCLUDED_\n#define _NGX_WIN32_CONFIG_H_INCLUDED_\n\n\n#undef  WIN32\n#define WIN32         0x0400\n#define _WIN32_WINNT  0x0501\n\n\n#define STRICT\n#define WIN32_LEAN_AND_MEAN\n\n/* enable getenv() and gmtime() in msvc8 */\n#define _CRT_SECURE_NO_WARNINGS\n#define _CRT_SECURE_NO_DEPRECATE\n\n/* enable gethostbyname() in msvc2015 */\n#if !(NGX_HAVE_INET6)\n#define _WINSOCK_DEPRECATED_NO_WARNINGS\n#endif\n\n/*\n * we need to include <windows.h> explicitly before <winsock2.h> because\n * the warning 4201 is enabled in <windows.h>\n */\n#include <windows.h>\n\n#ifdef _MSC_VER\n#pragma warning(disable:4201)\n#endif\n\n#include <winsock2.h>\n#include <ws2tcpip.h>  /* ipv6 */\n#include <mswsock.h>\n#include <shellapi.h>\n#include <stddef.h>    /* offsetof() */\n\n#ifdef __MINGW64_VERSION_MAJOR\n\n/* GCC MinGW-w64 supports _FILE_OFFSET_BITS */\n#define _FILE_OFFSET_BITS 64\n\n#elif defined __GNUC__\n\n/* GCC MinGW's stdio.h includes sys/types.h */\n#define _OFF_T_\n#define __have_typedef_off_t\n\n#endif\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#ifdef __GNUC__\n#include <stdint.h>\n#endif\n#include <ctype.h>\n#include <locale.h>\n\n#ifdef __WATCOMC__\n#define _TIME_T_DEFINED\ntypedef long  time_t;\n/* OpenWatcom defines time_t as \"unsigned long\" */\n#endif\n\n#include <time.h>      /* localtime(), strftime() */\n\n\n#ifdef _MSC_VER\n\n/* the end of the precompiled headers */\n#pragma hdrstop\n\n#pragma warning(default:4201)\n\n/* disable some \"-W4\" level warnings */\n\n/* 'type cast': from function pointer to data pointer */\n#pragma warning(disable:4054)\n\n/* 'type cast': from data pointer to function pointer */\n#pragma warning(disable:4055)\n\n/* 'function' : different 'const' qualifiers */\n#pragma warning(disable:4090)\n\n/* unreferenced formal parameter */\n#pragma warning(disable:4100)\n\n/* FD_SET() and FD_CLR(): conditional expression is constant */\n#pragma warning(disable:4127)\n\n/* conversion from 'type1' to 'type2', possible loss of data */\n#pragma warning(disable:4244)\n\n/* conversion from 'size_t' to 'type', possible loss of data */\n#pragma warning(disable:4267)\n\n/* array is too small to include a terminating null character */\n#pragma warning(disable:4295)\n\n#endif\n\n\n#ifdef __WATCOMC__\n\n/* symbol 'ngx_rbtree_min' has been defined, but not referenced */\n#pragma disable_message(202)\n\n#endif\n\n\n#ifdef __BORLANDC__\n\n/* the end of the precompiled headers */\n#pragma hdrstop\n\n/* functions containing (for|while|some if) are not expanded inline */\n#pragma warn -8027\n\n/* unreferenced formal parameter */\n#pragma warn -8057\n\n/* suspicious pointer arithmetic */\n#pragma warn -8072\n\n#endif\n\n\n#include <ngx_auto_config.h>\n\n\n#define ngx_inline          __inline\n#define ngx_cdecl           __cdecl\n\n\n#ifdef _MSC_VER\ntypedef unsigned __int32    uint32_t;\ntypedef __int32             int32_t;\ntypedef unsigned __int16    uint16_t;\n#define ngx_libc_cdecl      __cdecl\n\n#elif defined __BORLANDC__\ntypedef unsigned __int32    uint32_t;\ntypedef __int32             int32_t;\ntypedef unsigned __int16    uint16_t;\n#define ngx_libc_cdecl      __cdecl\n\n#else /* __WATCOMC__ */\ntypedef unsigned int        uint32_t;\ntypedef int                 int32_t;\ntypedef unsigned short int  uint16_t;\n#define ngx_libc_cdecl\n\n#endif\n\ntypedef __int64             int64_t;\ntypedef unsigned __int64    uint64_t;\n\n#if __BORLANDC__\ntypedef int                 intptr_t;\ntypedef u_int               uintptr_t;\n#endif\n\n\n#ifndef __MINGW64_VERSION_MAJOR\n\n/* Windows defines off_t as long, which is 32-bit */\ntypedef __int64             off_t;\n#define _OFF_T_DEFINED\n\n#endif\n\n\n#ifdef __WATCOMC__\n\n/* off_t is redefined by sys/types.h used by zlib.h */\n#define __TYPES_H_INCLUDED\ntypedef int                 dev_t;\ntypedef unsigned int        ino_t;\n\n#elif __BORLANDC__\n\n/* off_t is redefined by sys/types.h used by zlib.h */\n#define __TYPES_H\n\ntypedef int                 dev_t;\ntypedef unsigned int        ino_t;\n\n#endif\n\n\n#ifndef __GNUC__\n#ifdef _WIN64\ntypedef __int64             ssize_t;\n#else\ntypedef int                 ssize_t;\n#endif\n#endif\n\n\ntypedef uint32_t            in_addr_t;\ntypedef u_short             in_port_t;\ntypedef int                 sig_atomic_t;\n\n\n#ifdef _WIN64\n\n#define NGX_PTR_SIZE            8\n#define NGX_SIZE_T_LEN          (sizeof(\"-9223372036854775808\") - 1)\n#define NGX_MAX_SIZE_T_VALUE    9223372036854775807\n#define NGX_TIME_T_LEN          (sizeof(\"-9223372036854775808\") - 1)\n#define NGX_TIME_T_SIZE         8\n#define NGX_MAX_TIME_T_VALUE    9223372036854775807\n\n#else\n\n#define NGX_PTR_SIZE            4\n#define NGX_SIZE_T_LEN          (sizeof(\"-2147483648\") - 1)\n#define NGX_MAX_SIZE_T_VALUE    2147483647\n#define NGX_TIME_T_LEN          (sizeof(\"-2147483648\") - 1)\n#define NGX_TIME_T_SIZE         4\n#define NGX_MAX_TIME_T_VALUE    2147483647\n\n#endif\n\n\n#define NGX_OFF_T_LEN           (sizeof(\"-9223372036854775807\") - 1)\n#define NGX_MAX_OFF_T_VALUE     9223372036854775807\n#define NGX_SIG_ATOMIC_T_SIZE   4\n\n\n#define NGX_HAVE_LITTLE_ENDIAN  1\n#define NGX_HAVE_NONALIGNED     1\n\n\n#define NGX_WIN_NT        200000\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#ifndef NGX_HAVE_CASELESS_FILESYSTEM\n#define NGX_HAVE_CASELESS_FILESYSTEM  1\n#endif\n\n#ifndef NGX_HAVE_WIN32_TRANSMITPACKETS\n#define NGX_HAVE_WIN32_TRANSMITPACKETS  1\n#define NGX_HAVE_WIN32_TRANSMITFILE     0\n#endif\n\n#ifndef NGX_HAVE_WIN32_TRANSMITFILE\n#define NGX_HAVE_WIN32_TRANSMITFILE  1\n#endif\n\n#if (NGX_HAVE_WIN32_TRANSMITPACKETS) || (NGX_HAVE_WIN32_TRANSMITFILE)\n#define NGX_HAVE_SENDFILE  1\n#endif\n\n#ifndef NGX_HAVE_SO_SNDLOWAT\n/* setsockopt(SO_SNDLOWAT) returns error WSAENOPROTOOPT */\n#define NGX_HAVE_SO_SNDLOWAT         0\n#endif\n\n#ifndef NGX_HAVE_FIONREAD\n#define NGX_HAVE_FIONREAD            1\n#endif\n\n#define NGX_HAVE_GETADDRINFO         1\n\n#define ngx_random               rand\n#define ngx_debug_init()\n\n\n#endif /* _NGX_WIN32_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/win32/ngx_win32_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_uint_t  ngx_win32_version;\nngx_uint_t  ngx_ncpu;\nngx_uint_t  ngx_max_wsabufs;\nngx_int_t   ngx_max_sockets;\nngx_uint_t  ngx_inherited_nonblocking = 1;\nngx_uint_t  ngx_tcp_nodelay_and_tcp_nopush;\n\nchar        ngx_unique[NGX_INT32_LEN + 1];\n\n\nngx_os_io_t ngx_os_io = {\n    ngx_wsarecv,\n    ngx_wsarecv_chain,\n    ngx_udp_wsarecv,\n    ngx_wsasend,\n    NULL,\n    NULL,\n    ngx_wsasend_chain,\n    0\n};\n\n\ntypedef struct {\n    WORD  wServicePackMinor;\n    WORD  wSuiteMask;\n    BYTE  wProductType;\n} ngx_osviex_stub_t;\n\n\nstatic u_int               osviex;\nstatic OSVERSIONINFOEX     osvi;\n\n/* Should these pointers be per protocol ? */\nLPFN_ACCEPTEX              ngx_acceptex;\nLPFN_GETACCEPTEXSOCKADDRS  ngx_getacceptexsockaddrs;\nLPFN_TRANSMITFILE          ngx_transmitfile;\nLPFN_TRANSMITPACKETS       ngx_transmitpackets;\nLPFN_CONNECTEX             ngx_connectex;\nLPFN_DISCONNECTEX          ngx_disconnectex;\n\nstatic GUID ax_guid = WSAID_ACCEPTEX;\nstatic GUID as_guid = WSAID_GETACCEPTEXSOCKADDRS;\nstatic GUID tf_guid = WSAID_TRANSMITFILE;\nstatic GUID tp_guid = WSAID_TRANSMITPACKETS;\nstatic GUID cx_guid = WSAID_CONNECTEX;\nstatic GUID dx_guid = WSAID_DISCONNECTEX;\n\n\n#if (NGX_LOAD_WSAPOLL)\nngx_wsapoll_pt             WSAPoll;\nngx_uint_t                 ngx_have_wsapoll;\n#endif\n\n\nngx_int_t\nngx_os_init(ngx_log_t *log)\n{\n    DWORD         bytes;\n    SOCKET        s;\n    WSADATA       wsd;\n    ngx_err_t     err;\n    ngx_time_t   *tp;\n    ngx_uint_t    n;\n    SYSTEM_INFO   si;\n\n    /* get Windows version */\n\n    ngx_memzero(&osvi, sizeof(OSVERSIONINFOEX));\n    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\n\n#ifdef _MSC_VER\n#pragma warning(disable:4996)\n#endif\n\n    osviex = GetVersionEx((OSVERSIONINFO *) &osvi);\n\n    if (osviex == 0) {\n        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);\n        if (GetVersionEx((OSVERSIONINFO *) &osvi) == 0) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          \"GetVersionEx() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n#ifdef _MSC_VER\n#pragma warning(default:4996)\n#endif\n\n    /*\n     *  Windows 3.1 Win32s   0xxxxx\n     *\n     *  Windows 95           140000\n     *  Windows 98           141000\n     *  Windows ME           149000\n     *  Windows NT 3.51      235100\n     *  Windows NT 4.0       240000\n     *  Windows NT 4.0 SP5   240050\n     *  Windows 2000         250000\n     *  Windows XP           250100\n     *  Windows 2003         250200\n     *  Windows Vista/2008   260000\n     *\n     *  Windows CE x.x       3xxxxx\n     */\n\n    ngx_win32_version = osvi.dwPlatformId * 100000\n                        + osvi.dwMajorVersion * 10000\n                        + osvi.dwMinorVersion * 100;\n\n    if (osviex) {\n        ngx_win32_version += osvi.wServicePackMajor * 10\n                             + osvi.wServicePackMinor;\n    }\n\n    GetSystemInfo(&si);\n    ngx_pagesize = si.dwPageSize;\n    ngx_allocation_granularity = si.dwAllocationGranularity;\n    ngx_ncpu = si.dwNumberOfProcessors;\n    ngx_cacheline_size = NGX_CPU_CACHE_LINE;\n\n    for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }\n\n    /* delete default \"C\" locale for _wcsicmp() */\n    setlocale(LC_ALL, \"\");\n\n\n    /* init Winsock */\n\n    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                      \"WSAStartup() failed\");\n        return NGX_ERROR;\n    }\n\n    if (ngx_win32_version < NGX_WIN_NT) {\n        ngx_max_wsabufs = 16;\n        return NGX_OK;\n    }\n\n    /* STUB: ngx_uint_t max */\n    ngx_max_wsabufs = 1024 * 1024;\n\n    /*\n     * get AcceptEx(), GetAcceptExSockAddrs(), TransmitFile(),\n     * TransmitPackets(), ConnectEx(), and DisconnectEx() addresses\n     */\n\n    s = ngx_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);\n    if (s == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                      ngx_socket_n \" failed\");\n        return NGX_ERROR;\n    }\n\n    if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &ax_guid, sizeof(GUID),\n                 &ngx_acceptex, sizeof(LPFN_ACCEPTEX), &bytes, NULL, NULL)\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno,\n                      \"WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, \"\n                               \"WSAID_ACCEPTEX) failed\");\n    }\n\n    if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &as_guid, sizeof(GUID),\n                 &ngx_getacceptexsockaddrs, sizeof(LPFN_GETACCEPTEXSOCKADDRS),\n                 &bytes, NULL, NULL)\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno,\n                      \"WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, \"\n                               \"WSAID_GETACCEPTEXSOCKADDRS) failed\");\n    }\n\n    if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &tf_guid, sizeof(GUID),\n                 &ngx_transmitfile, sizeof(LPFN_TRANSMITFILE), &bytes,\n                 NULL, NULL)\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno,\n                      \"WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, \"\n                               \"WSAID_TRANSMITFILE) failed\");\n    }\n\n    if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &tp_guid, sizeof(GUID),\n                 &ngx_transmitpackets, sizeof(LPFN_TRANSMITPACKETS), &bytes,\n                 NULL, NULL)\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno,\n                      \"WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, \"\n                               \"WSAID_TRANSMITPACKETS) failed\");\n    }\n\n    if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &cx_guid, sizeof(GUID),\n                 &ngx_connectex, sizeof(LPFN_CONNECTEX), &bytes,\n                 NULL, NULL)\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno,\n                      \"WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, \"\n                               \"WSAID_CONNECTEX) failed\");\n    }\n\n    if (WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &dx_guid, sizeof(GUID),\n                 &ngx_disconnectex, sizeof(LPFN_DISCONNECTEX), &bytes,\n                 NULL, NULL)\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_socket_errno,\n                      \"WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, \"\n                               \"WSAID_DISCONNECTEX) failed\");\n    }\n\n    if (ngx_close_socket(s) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,\n                      ngx_close_socket_n \" failed\");\n    }\n\n#if (NGX_LOAD_WSAPOLL)\n    {\n    HMODULE  hmod;\n\n    hmod = GetModuleHandle(\"ws2_32.dll\");\n    if (hmod == NULL) {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_errno,\n                      \"GetModuleHandle(\\\"ws2_32.dll\\\") failed\");\n        goto nopoll;\n    }\n\n    WSAPoll = (ngx_wsapoll_pt) (void *) GetProcAddress(hmod, \"WSAPoll\");\n    if (WSAPoll == NULL) {\n        ngx_log_error(NGX_LOG_NOTICE, log, ngx_errno,\n                      \"GetProcAddress(\\\"WSAPoll\\\") failed\");\n        goto nopoll;\n    }\n\n    ngx_have_wsapoll = 1;\n\n    }\n\nnopoll:\n\n#endif\n\n    if (GetEnvironmentVariable(\"ngx_unique\", ngx_unique, NGX_INT32_LEN + 1)\n        != 0)\n    {\n        ngx_process = NGX_PROCESS_WORKER;\n\n    } else {\n        err = ngx_errno;\n\n        if (err != ERROR_ENVVAR_NOT_FOUND) {\n            ngx_log_error(NGX_LOG_EMERG, log, err,\n                          \"GetEnvironmentVariable(\\\"ngx_unique\\\") failed\");\n            return NGX_ERROR;\n        }\n\n        ngx_sprintf((u_char *) ngx_unique, \"%P%Z\", ngx_pid);\n    }\n\n    tp = ngx_timeofday();\n    srand((ngx_pid << 16) ^ (unsigned) tp->sec ^ tp->msec);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_os_status(ngx_log_t *log)\n{\n    ngx_osviex_stub_t  *osviex_stub;\n\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, NGINX_VER_BUILD);\n\n    if (osviex) {\n\n        /*\n         * the MSVC 6.0 SP2 defines wSuiteMask and wProductType\n         * as WORD wReserved[2]\n         */\n        osviex_stub = (ngx_osviex_stub_t *) &osvi.wServicePackMinor;\n\n        ngx_log_error(NGX_LOG_INFO, log, 0,\n                      \"OS: %ui build:%ud, \\\"%s\\\", suite:%Xd, type:%ud\",\n                      ngx_win32_version, osvi.dwBuildNumber, osvi.szCSDVersion,\n                      osviex_stub->wSuiteMask, osviex_stub->wProductType);\n\n    } else {\n        if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {\n\n            /* Win9x build */\n\n            ngx_log_error(NGX_LOG_INFO, log, 0,\n                          \"OS: %ui build:%ud.%ud.%ud, \\\"%s\\\"\",\n                          ngx_win32_version,\n                          osvi.dwBuildNumber >> 24,\n                          (osvi.dwBuildNumber >> 16) & 0xff,\n                          osvi.dwBuildNumber & 0xffff,\n                          osvi.szCSDVersion);\n\n        } else {\n\n            /*\n             * VER_PLATFORM_WIN32_NT\n             *\n             * we do not currently support VER_PLATFORM_WIN32_CE\n             * and we do not support VER_PLATFORM_WIN32s at all\n             */\n\n            ngx_log_error(NGX_LOG_INFO, log, 0, \"OS: %ui build:%ud, \\\"%s\\\"\",\n                          ngx_win32_version, osvi.dwBuildNumber,\n                          osvi.szCSDVersion);\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/win32/ngx_wsarecv.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_wsarecv(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int           rc;\n    u_long        bytes, flags;\n    WSABUF        wsabuf[1];\n    ngx_err_t     err;\n    ngx_int_t     n;\n    ngx_event_t  *rev;\n\n    wsabuf[0].buf = (char *) buf;\n    wsabuf[0].len = size;\n    flags = 0;\n    bytes = 0;\n\n    rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, NULL, NULL);\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"WSARecv: fd:%d rc:%d %ul of %z\", c->fd, rc, bytes, size);\n\n    rev = c->read;\n\n    if (rc == -1) {\n        rev->ready = 0;\n        err = ngx_socket_errno;\n\n        if (err == WSAEWOULDBLOCK) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"WSARecv() not ready\");\n            return NGX_AGAIN;\n        }\n\n        n = ngx_connection_error(c, err, \"WSARecv() failed\");\n\n        if (n == NGX_ERROR) {\n            rev->error = 1;\n        }\n\n        return n;\n    }\n\n#if (NGX_HAVE_FIONREAD)\n\n    if (rev->available >= 0 && bytes > 0) {\n        rev->available -= bytes;\n\n        /*\n         * negative rev->available means some additional bytes\n         * were received between kernel notification and WSARecv(),\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                       \"WSARecv: avail:%d\", rev->available);\n\n    } else if (bytes == 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\n            if (n == NGX_ERROR) {\n                rev->error = 1;\n            }\n\n            return n;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"WSARecv: avail:%d\", rev->available);\n    }\n\n#endif\n\n    if (bytes < size) {\n        rev->ready = 0;\n    }\n\n    if (bytes == 0) {\n        rev->eof = 1;\n    }\n\n    return bytes;\n}\n\n\nssize_t\nngx_overlapped_wsarecv(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int               rc;\n    u_long            bytes, flags;\n    WSABUF            wsabuf[1];\n    ngx_err_t         err;\n    ngx_int_t         n;\n    ngx_event_t      *rev;\n    LPWSAOVERLAPPED   ovlp;\n\n    rev = c->read;\n\n    if (!rev->ready) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"second wsa 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\n    if (rev->complete) {\n        rev->complete = 0;\n\n        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n            if (rev->ovlp.error) {\n                ngx_connection_error(c, rev->ovlp.error, \"WSARecv() failed\");\n                return NGX_ERROR;\n            }\n\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"WSARecv ovlp: fd:%d %ul of %z\",\n                           c->fd, rev->available, size);\n\n            return rev->available;\n        }\n\n        if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &rev->ovlp,\n                                   &bytes, 0, NULL)\n            == 0)\n        {\n            ngx_connection_error(c, ngx_socket_errno,\n                               \"WSARecv() or WSAGetOverlappedResult() failed\");\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"WSARecv: fd:%d %ul of %z\", c->fd, bytes, size);\n\n        return bytes;\n    }\n\n    ovlp = (LPWSAOVERLAPPED) &rev->ovlp;\n    ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));\n    wsabuf[0].buf = (char *) buf;\n    wsabuf[0].len = size;\n    flags = 0;\n    bytes = 0;\n\n    rc = WSARecv(c->fd, wsabuf, 1, &bytes, &flags, ovlp, NULL);\n\n    rev->complete = 0;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"WSARecv ovlp: fd:%d rc:%d %ul of %z\",\n                   c->fd, rc, bytes, size);\n\n    if (rc == -1) {\n        err = ngx_socket_errno;\n        if (err == WSA_IO_PENDING) {\n            rev->active = 1;\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"WSARecv() posted\");\n            return NGX_AGAIN;\n        }\n\n        n = ngx_connection_error(c, err, \"WSARecv() failed\");\n\n        if (n == NGX_ERROR) {\n            rev->error = 1;\n        }\n\n        return n;\n    }\n\n    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n\n        /*\n         * if a socket was bound with I/O completion port\n         * then GetQueuedCompletionStatus() would anyway return its status\n         * despite that WSARecv() was already complete\n         */\n\n        rev->active = 1;\n        return NGX_AGAIN;\n    }\n\n    if (bytes == 0) {\n        rev->eof = 1;\n        rev->ready = 0;\n\n    } else {\n        rev->ready = 1;\n    }\n\n    rev->active = 0;\n\n    return bytes;\n}\n"
  },
  {
    "path": "src/os/win32/ngx_wsarecv_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#define NGX_WSABUFS  64\n\n\nssize_t\nngx_wsarecv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit)\n{\n    int           rc;\n    u_char       *prev;\n    u_long        bytes, flags;\n    size_t        n, size;\n    ngx_err_t     err;\n    ngx_array_t   vec;\n    ngx_event_t  *rev;\n    LPWSABUF      wsabuf;\n    WSABUF        wsabufs[NGX_WSABUFS];\n\n    prev = NULL;\n    wsabuf = NULL;\n    flags = 0;\n    size = 0;\n    bytes = 0;\n\n    vec.elts = wsabufs;\n    vec.nelts = 0;\n    vec.size = sizeof(WSABUF);\n    vec.nalloc = NGX_WSABUFS;\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 >= (size_t) limit) {\n                break;\n            }\n\n            if (size + n > (size_t) limit) {\n                n = (size_t) limit - size;\n            }\n        }\n\n        if (prev == chain->buf->last) {\n            wsabuf->len += n;\n\n        } else {\n            if (vec.nelts == vec.nalloc) {\n                break;\n            }\n\n            wsabuf = ngx_array_push(&vec);\n            if (wsabuf == NULL) {\n                return NGX_ERROR;\n            }\n\n            wsabuf->buf = (char *) chain->buf->last;\n            wsabuf->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                   \"WSARecv: %d:%d\", vec.nelts, wsabuf->len);\n\n\n    rc = WSARecv(c->fd, vec.elts, vec.nelts, &bytes, &flags, NULL, NULL);\n\n    rev = c->read;\n\n    if (rc == -1) {\n        rev->ready = 0;\n        err = ngx_socket_errno;\n\n        if (err == WSAEWOULDBLOCK) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"WSARecv() not ready\");\n            return NGX_AGAIN;\n        }\n\n        rev->error = 1;\n        ngx_connection_error(c, err, \"WSARecv() failed\");\n        return NGX_ERROR;\n    }\n\n#if (NGX_HAVE_FIONREAD)\n\n    if (rev->available >= 0 && bytes > 0) {\n        rev->available -= bytes;\n\n        /*\n         * negative rev->available means some additional bytes\n         * were received between kernel notification and WSARecv(),\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                       \"WSARecv: avail:%d\", rev->available);\n\n    } else if (bytes == size) {\n\n        if (ngx_socket_nread(c->fd, &rev->available) == -1) {\n            rev->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                       \"WSARecv: avail:%d\", rev->available);\n    }\n\n#endif\n\n    if (bytes < size) {\n        rev->ready = 0;\n    }\n\n    if (bytes == 0) {\n        rev->eof = 1;\n    }\n\n    return bytes;\n}\n"
  },
  {
    "path": "src/os/win32/ngx_wsasend.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_wsasend(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int           n;\n    u_long        sent;\n    ngx_err_t     err;\n    ngx_event_t  *wev;\n    WSABUF        wsabuf;\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return NGX_AGAIN;\n    }\n\n    /*\n     * WSABUF must be 4-byte aligned otherwise\n     * WSASend() will return undocumented WSAEINVAL error.\n     */\n\n    wsabuf.buf = (char *) buf;\n    wsabuf.len = size;\n\n    sent = 0;\n\n    n = WSASend(c->fd, &wsabuf, 1, &sent, 0, NULL, NULL);\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"WSASend: fd:%d, %d, %ul of %uz\", c->fd, n, sent, size);\n\n    if (n == 0) {\n        if (sent < size) {\n            wev->ready = 0;\n        }\n\n        c->sent += sent;\n\n        return sent;\n    }\n\n    err = ngx_socket_errno;\n\n    if (err == WSAEWOULDBLOCK) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, \"WSASend() not ready\");\n        wev->ready = 0;\n        return NGX_AGAIN;\n    }\n\n    wev->error = 1;\n    ngx_connection_error(c, err, \"WSASend() failed\");\n\n    return NGX_ERROR;\n}\n\n\nssize_t\nngx_overlapped_wsasend(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int               n;\n    u_long            sent;\n    ngx_err_t         err;\n    ngx_event_t      *wev;\n    LPWSAOVERLAPPED   ovlp;\n    WSABUF            wsabuf;\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"wev->complete: %d\", wev->complete);\n\n    if (!wev->complete) {\n\n        /* post the overlapped WSASend() */\n\n        /*\n         * WSABUFs must be 4-byte aligned otherwise\n         * WSASend() will return undocumented WSAEINVAL error.\n         */\n\n        wsabuf.buf = (char *) buf;\n        wsabuf.len = size;\n\n        sent = 0;\n\n        ovlp = (LPWSAOVERLAPPED) &c->write->ovlp;\n        ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));\n\n        n = WSASend(c->fd, &wsabuf, 1, &sent, 0, ovlp, NULL);\n\n        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"WSASend: fd:%d, %d, %ul of %uz\", c->fd, n, sent, size);\n\n        wev->complete = 0;\n\n        if (n == 0) {\n            if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n\n                /*\n                 * if a socket was bound with I/O completion port then\n                 * GetQueuedCompletionStatus() would anyway return its status\n                 * despite that WSASend() was already complete\n                 */\n\n                wev->active = 1;\n                return NGX_AGAIN;\n            }\n\n            if (sent < size) {\n                wev->ready = 0;\n            }\n\n            c->sent += sent;\n\n            return sent;\n        }\n\n        err = ngx_socket_errno;\n\n        if (err == WSA_IO_PENDING) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"WSASend() posted\");\n            wev->active = 1;\n            return NGX_AGAIN;\n        }\n\n        wev->error = 1;\n        ngx_connection_error(c, err, \"WSASend() failed\");\n\n        return NGX_ERROR;\n    }\n\n    /* the overlapped WSASend() complete */\n\n    wev->complete = 0;\n    wev->active = 0;\n\n    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n\n        if (wev->ovlp.error) {\n            ngx_connection_error(c, wev->ovlp.error, \"WSASend() failed\");\n            return NGX_ERROR;\n        }\n\n        sent = wev->available;\n\n    } else {\n        if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &wev->ovlp,\n                                   &sent, 0, NULL)\n            == 0)\n        {\n            ngx_connection_error(c, ngx_socket_errno,\n                           \"WSASend() or WSAGetOverlappedResult() failed\");\n\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"WSAGetOverlappedResult: fd:%d, %ul of %uz\",\n                   c->fd, sent, size);\n\n    if (sent < size) {\n        wev->ready = 0;\n    }\n\n    c->sent += sent;\n\n    return sent;\n}\n"
  },
  {
    "path": "src/os/win32/ngx_wsasend_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#define NGX_WSABUFS  64\n\n\nngx_chain_t *\nngx_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    int           rc;\n    u_char       *prev;\n    u_long        size, sent, send, prev_send;\n    ngx_err_t     err;\n    ngx_event_t  *wev;\n    ngx_array_t   vec;\n    ngx_chain_t  *cl;\n    LPWSABUF      wsabuf;\n    WSABUF        wsabufs[NGX_WSABUFS];\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n    /* the maximum limit size is the maximum u_long value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_UINT32_VALUE - ngx_pagesize;\n    }\n\n    send = 0;\n\n    /*\n     * WSABUFs must be 4-byte aligned otherwise\n     * WSASend() will return undocumented WSAEINVAL error.\n     */\n\n    vec.elts = wsabufs;\n    vec.size = sizeof(WSABUF);\n    vec.nalloc = ngx_min(NGX_WSABUFS, ngx_max_wsabufs);\n    vec.pool = c->pool;\n\n    for ( ;; ) {\n        prev = NULL;\n        wsabuf = NULL;\n        prev_send = send;\n\n        vec.nelts = 0;\n\n        /* create the WSABUF 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            size = cl->buf->last - cl->buf->pos;\n\n            if (send + size > limit) {\n                size = (u_long) (limit - send);\n            }\n\n            if (prev == cl->buf->pos) {\n                wsabuf->len += cl->buf->last - cl->buf->pos;\n\n            } else {\n                if (vec.nelts == vec.nalloc) {\n                    break;\n                }\n\n                wsabuf = ngx_array_push(&vec);\n                if (wsabuf == NULL) {\n                    return NGX_CHAIN_ERROR;\n                }\n\n                wsabuf->buf = (char *) cl->buf->pos;\n                wsabuf->len = cl->buf->last - cl->buf->pos;\n            }\n\n            prev = cl->buf->last;\n            send += size;\n        }\n\n        sent = 0;\n\n        rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, NULL, NULL);\n\n        if (rc == -1) {\n            err = ngx_errno;\n\n            if (err == WSAEWOULDBLOCK) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                               \"WSASend() not ready\");\n\n            } else {\n                wev->error = 1;\n                ngx_connection_error(c, err, \"WSASend() failed\");\n                return NGX_CHAIN_ERROR;\n            }\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"WSASend: fd:%d, s:%ul\", c->fd, sent);\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_overlapped_wsasend_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    int               rc;\n    u_char           *prev;\n    u_long            size, send, sent;\n    ngx_err_t         err;\n    ngx_event_t      *wev;\n    ngx_array_t       vec;\n    ngx_chain_t      *cl;\n    LPWSAOVERLAPPED   ovlp;\n    LPWSABUF          wsabuf;\n    WSABUF            wsabufs[NGX_WSABUFS];\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"wev->complete: %d\", wev->complete);\n\n    if (!wev->complete) {\n\n        /* post the overlapped WSASend() */\n\n        /* the maximum limit size is the maximum u_long value - the page size */\n\n        if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize))\n        {\n            limit = NGX_MAX_UINT32_VALUE - ngx_pagesize;\n        }\n\n        /*\n         * WSABUFs must be 4-byte aligned otherwise\n         * WSASend() will return undocumented WSAEINVAL error.\n         */\n\n        vec.elts = wsabufs;\n        vec.nelts = 0;\n        vec.size = sizeof(WSABUF);\n        vec.nalloc = ngx_min(NGX_WSABUFS, ngx_max_wsabufs);\n        vec.pool = c->pool;\n\n        send = 0;\n        prev = NULL;\n        wsabuf = NULL;\n\n        /* create the WSABUF 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            size = cl->buf->last - cl->buf->pos;\n\n            if (send + size > limit) {\n                size = (u_long) (limit - send);\n            }\n\n            if (prev == cl->buf->pos) {\n                wsabuf->len += cl->buf->last - cl->buf->pos;\n\n            } else {\n                if (vec.nelts == vec.nalloc) {\n                    break;\n                }\n\n                wsabuf = ngx_array_push(&vec);\n                if (wsabuf == NULL) {\n                    return NGX_CHAIN_ERROR;\n                }\n\n                wsabuf->buf = (char *) cl->buf->pos;\n                wsabuf->len = cl->buf->last - cl->buf->pos;\n            }\n\n            prev = cl->buf->last;\n            send += size;\n        }\n\n        ovlp = (LPWSAOVERLAPPED) &c->write->ovlp;\n        ngx_memzero(ovlp, sizeof(WSAOVERLAPPED));\n\n        rc = WSASend(c->fd, vec.elts, vec.nelts, &sent, 0, ovlp, NULL);\n\n        wev->complete = 0;\n\n        if (rc == -1) {\n            err = ngx_errno;\n\n            if (err == WSA_IO_PENDING) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                               \"WSASend() posted\");\n                wev->active = 1;\n                return in;\n\n            } else {\n                wev->error = 1;\n                ngx_connection_error(c, err, \"WSASend() failed\");\n                return NGX_CHAIN_ERROR;\n            }\n\n        } else if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n\n            /*\n             * if a socket was bound with I/O completion port then\n             * GetQueuedCompletionStatus() would anyway return its status\n             * despite that WSASend() was already complete\n             */\n\n            wev->active = 1;\n            return in;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"WSASend: fd:%d, s:%ul\", c->fd, sent);\n\n    } else {\n\n        /* the overlapped WSASend() complete */\n\n        wev->complete = 0;\n        wev->active = 0;\n\n        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n            if (wev->ovlp.error) {\n                ngx_connection_error(c, wev->ovlp.error, \"WSASend() failed\");\n                return NGX_CHAIN_ERROR;\n            }\n\n            sent = wev->available;\n\n        } else {\n            if (WSAGetOverlappedResult(c->fd, (LPWSAOVERLAPPED) &wev->ovlp,\n                                       &sent, 0, NULL)\n                == 0)\n            {\n                ngx_connection_error(c, ngx_socket_errno,\n                               \"WSASend() or WSAGetOverlappedResult() failed\");\n\n                return NGX_CHAIN_ERROR;\n            }\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"WSASend ovlp: fd:%d, s:%ul\", c->fd, sent);\n\n    c->sent += sent;\n\n    in = ngx_chain_update_sent(in, sent);\n\n    if (in) {\n        wev->ready = 0;\n\n    } else {\n        wev->ready = 1;\n    }\n\n    return in;\n}\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\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\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\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_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    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#if (NGX_STREAM_QUIC)\n            ls->quic = addr[i].opt.quic;\n#endif\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            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\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#if (NGX_STREAM_QUIC)\n        addrs[i].conf.quic = addr[i].opt.quic;\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_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\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#if (NGX_STREAM_QUIC)\n        addrs6[i].conf.quic = addr[i].opt.quic;\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_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#if (NGX_STREAM_QUIC)\n#include <ngx_stream_quic_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    unsigned                       quic: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} 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                       quic:1;\n    unsigned                       proxy_protocol:1;\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} 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} 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} ngx_stream_core_srv_conf_t;\n\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\n    unsigned                       limit_conn_status:2;\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\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      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    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    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    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\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    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;\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_n(&cmcf->listen, u.naddrs);\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 (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_strcmp(value[i].data, \"quic\") == 0) {\n#if (NGX_STREAM_QUIC)\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->quic = 1;\n            ls->type = SOCK_DGRAM;\n\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"quic\\\" parameter requires \"\n                               \"ngx_stream_quic_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 (ls->ssl) {\n            return \"\\\"ssl\\\" parameter is incompatible with \\\"udp\\\"\";\n        }\n#endif\n\n#if (NGX_STREAM_SSL && NGX_STREAM_QUIC)\n        if (ls->ssl && ls->quic) {\n            return \"\\\"ssl\\\" parameter is incompatible with \\\"quic\\\"\";\n        }\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    als = cmcf->listen.elts;\n\n    for (n = 0; n < u.naddrs; n++) {\n        ls[n] = ls[0];\n\n        ls[n].sockaddr = u.addrs[n].sockaddr;\n        ls[n].socklen = u.addrs[n].socklen;\n        ls[n].addr_text = u.addrs[n].name;\n        ls[n].wildcard = ngx_inet_wildcard(ls[n].sockaddr);\n\n        for (i = 0; i < cmcf->listen.nelts - u.naddrs + n; i++) {\n            if (ls[n].type != als[i].type) {\n                continue;\n            }\n\n            if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,\n                                 ls[n].sockaddr, ls[n].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                               &ls[n].addr_text);\n            return NGX_CONF_ERROR;\n        }\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"
  },
  {
    "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 (NGX_STREAM_SSL)\n    s->ssl = addr_conf->ssl;\n#endif\n\n#if (NGX_STREAM_QUIC)\n    s->ssl |= addr_conf->quic;\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#if (NGX_STREAM_QUIC)\n\n    if (addr_conf->quic) {\n        ngx_quic_conf_t  *qcf;\n\n        if (c->quic == NULL) {\n            qcf = ngx_stream_get_module_srv_conf(addr_conf->ctx,\n                                                 ngx_stream_quic_module);\n            ngx_quic_run(c, qcf);\n            return;\n        }\n    }\n\n#endif\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 (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    }\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#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_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    { 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#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) {\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_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, p + NGX_PROXY_PROTOCOL_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_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, buf + NGX_PROXY_PROTOCOL_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 (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 && (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            && !src->read->error)\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 = 1;\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\n#if (NGX_STREAM_QUIC)\n            if (dst->quic) {\n\n                if (ngx_quic_shutdown_stream(dst, NGX_WRITE_SHUTDOWN)\n                    != NGX_OK)\n                {\n                    ngx_stream_proxy_finalize(s,\n                                             NGX_STREAM_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n\n            } else\n#endif\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#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    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|NGX_SSL_TLSv1\n                               |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));\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#endif\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_STREAM_SSL)\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    pscf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (pscf->ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    pscf->ssl->log = cf->log;\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\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            if (ngx_ssl_certificate(cf, pscf->ssl,\n                                    &pscf->ssl_certificate->value,\n                                    &pscf->ssl_certificate_key->value,\n                                    pscf->ssl_passwords)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\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_quic_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Roman Arutyunyan\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_variable_quic(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_quic_add_variables(ngx_conf_t *cf);\nstatic void *ngx_stream_quic_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_quic_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_stream_quic_max_ack_delay(ngx_conf_t *cf, void *post,\n    void *data);\nstatic char *ngx_stream_quic_max_udp_payload_size(ngx_conf_t *cf, void *post,\n    void *data);\nstatic char *ngx_stream_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_conf_post_t  ngx_stream_quic_max_ack_delay_post =\n    { ngx_stream_quic_max_ack_delay };\nstatic ngx_conf_post_t  ngx_stream_quic_max_udp_payload_size_post =\n    { ngx_stream_quic_max_udp_payload_size };\nstatic ngx_conf_num_bounds_t  ngx_stream_quic_ack_delay_exponent_bounds =\n    { ngx_conf_check_num_bounds, 0, 20 };\nstatic ngx_conf_num_bounds_t\n                            ngx_stream_quic_active_connection_id_limit_bounds =\n    { ngx_conf_check_num_bounds, 2, -1 };\n\n\nstatic ngx_command_t  ngx_stream_quic_commands[] = {\n\n    { ngx_string(\"quic_max_idle_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_quic_conf_t, tp.max_idle_timeout),\n      NULL },\n\n    { ngx_string(\"quic_max_ack_delay\"),\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_quic_conf_t, tp.max_ack_delay),\n      &ngx_stream_quic_max_ack_delay_post },\n\n    { ngx_string(\"quic_max_udp_payload_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_quic_conf_t, tp.max_udp_payload_size),\n      &ngx_stream_quic_max_udp_payload_size_post },\n\n    { ngx_string(\"quic_initial_max_data\"),\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_quic_conf_t, tp.initial_max_data),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_stream_data_bidi_local\"),\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_quic_conf_t, tp.initial_max_stream_data_bidi_local),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_stream_data_bidi_remote\"),\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_quic_conf_t, tp.initial_max_stream_data_bidi_remote),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_stream_data_uni\"),\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_quic_conf_t, tp.initial_max_stream_data_uni),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_streams_bidi\"),\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_quic_conf_t, tp.initial_max_streams_bidi),\n      NULL },\n\n    { ngx_string(\"quic_initial_max_streams_uni\"),\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_quic_conf_t, tp.initial_max_streams_uni),\n      NULL },\n\n    { ngx_string(\"quic_ack_delay_exponent\"),\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_quic_conf_t, tp.ack_delay_exponent),\n      &ngx_stream_quic_ack_delay_exponent_bounds },\n\n    { ngx_string(\"quic_disable_active_migration\"),\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_quic_conf_t, tp.disable_active_migration),\n      NULL },\n\n    { ngx_string(\"quic_active_connection_id_limit\"),\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_quic_conf_t, tp.active_connection_id_limit),\n      &ngx_stream_quic_active_connection_id_limit_bounds },\n\n    { ngx_string(\"quic_retry\"),\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_quic_conf_t, retry),\n      NULL },\n\n    { ngx_string(\"quic_gso\"),\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_quic_conf_t, gso_enabled),\n      NULL },\n\n    { ngx_string(\"quic_host_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_stream_quic_host_key,\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_quic_module_ctx = {\n    ngx_stream_quic_add_variables,         /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_quic_create_srv_conf,       /* create server configuration */\n    ngx_stream_quic_merge_srv_conf,        /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_quic_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_quic_module_ctx,           /* module context */\n    ngx_stream_quic_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_quic_vars[] = {\n\n    { ngx_string(\"quic\"), NULL, ngx_stream_variable_quic, 0, 0, 0 },\n\n      ngx_stream_null_variable\n};\n\nstatic ngx_str_t  ngx_stream_quic_salt = ngx_string(\"ngx_quic\");\n\n\nstatic ngx_int_t\nngx_stream_variable_quic(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    if (s->connection->quic) {\n\n        v->len = 4;\n        v->valid = 1;\n        v->no_cacheable = 1;\n        v->not_found = 0;\n        v->data = (u_char *) \"quic\";\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_quic_add_variables(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t  *var, *v;\n\n    for (v = ngx_stream_quic_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_quic_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_quic_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_quic_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->tp.original_dcid = { 0, NULL };\n     *     conf->tp.initial_scid = { 0, NULL };\n     *     conf->tp.retry_scid = { 0, NULL };\n     *     conf->tp.preferred_address = NULL\n     *     conf->host_key = { 0, NULL }\n     *     conf->stream_close_code = 0;\n     *     conf->stream_reject_code_uni = 0;\n     *     conf->stream_reject_code_bidi= 0;\n     */\n\n    conf->tp.max_idle_timeout = NGX_CONF_UNSET_MSEC;\n    conf->tp.max_ack_delay = NGX_CONF_UNSET_MSEC;\n    conf->tp.max_udp_payload_size = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_data = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_stream_data_uni = NGX_CONF_UNSET_SIZE;\n    conf->tp.initial_max_streams_bidi = NGX_CONF_UNSET_UINT;\n    conf->tp.initial_max_streams_uni = NGX_CONF_UNSET_UINT;\n    conf->tp.ack_delay_exponent = NGX_CONF_UNSET_UINT;\n    conf->tp.disable_active_migration = NGX_CONF_UNSET;\n    conf->tp.active_connection_id_limit = NGX_CONF_UNSET_UINT;\n\n    conf->retry = NGX_CONF_UNSET;\n    conf->gso_enabled = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_quic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_quic_conf_t *prev = parent;\n    ngx_quic_conf_t *conf = child;\n\n    ngx_stream_ssl_conf_t  *scf;\n\n    ngx_conf_merge_msec_value(conf->tp.max_idle_timeout,\n                              prev->tp.max_idle_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->tp.max_ack_delay,\n                              prev->tp.max_ack_delay,\n                              NGX_QUIC_DEFAULT_MAX_ACK_DELAY);\n\n    ngx_conf_merge_size_value(conf->tp.max_udp_payload_size,\n                              prev->tp.max_udp_payload_size,\n                              NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_data,\n                              prev->tp.initial_max_data,\n                              16 * NGX_QUIC_STREAM_BUFSIZE);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_local,\n                              prev->tp.initial_max_stream_data_bidi_local,\n                              NGX_QUIC_STREAM_BUFSIZE);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_bidi_remote,\n                              prev->tp.initial_max_stream_data_bidi_remote,\n                              NGX_QUIC_STREAM_BUFSIZE);\n\n    ngx_conf_merge_size_value(conf->tp.initial_max_stream_data_uni,\n                              prev->tp.initial_max_stream_data_uni,\n                              NGX_QUIC_STREAM_BUFSIZE);\n\n    ngx_conf_merge_uint_value(conf->tp.initial_max_streams_bidi,\n                              prev->tp.initial_max_streams_bidi, 16);\n\n    ngx_conf_merge_uint_value(conf->tp.initial_max_streams_uni,\n                              prev->tp.initial_max_streams_uni, 16);\n\n    ngx_conf_merge_uint_value(conf->tp.ack_delay_exponent,\n                              prev->tp.ack_delay_exponent,\n                              NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT);\n\n    ngx_conf_merge_value(conf->tp.disable_active_migration,\n                              prev->tp.disable_active_migration, 0);\n\n    ngx_conf_merge_uint_value(conf->tp.active_connection_id_limit,\n                              prev->tp.active_connection_id_limit, 2);\n\n    ngx_conf_merge_value(conf->retry, prev->retry, 0);\n    ngx_conf_merge_value(conf->gso_enabled, prev->gso_enabled, 0);\n\n    ngx_conf_merge_str_value(conf->host_key, prev->host_key, \"\");\n\n    if (conf->host_key.len == 0) {\n\n        conf->host_key.len = NGX_QUIC_DEFAULT_HOST_KEY_LEN;\n        conf->host_key.data = ngx_palloc(cf->pool, conf->host_key.len);\n        if (conf->host_key.data == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (RAND_bytes(conf->host_key.data, NGX_QUIC_DEFAULT_HOST_KEY_LEN)\n            <= 0)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_quic_derive_key(cf->log, \"av_token_key\",\n                            &conf->host_key, &ngx_stream_quic_salt,\n                            conf->av_token_key, NGX_QUIC_AV_KEY_LEN)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_quic_derive_key(cf->log, \"sr_token_key\",\n                            &conf->host_key, &ngx_stream_quic_salt,\n                            conf->sr_token_key, NGX_QUIC_SR_KEY_LEN)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    scf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_ssl_module);\n    conf->ssl = &scf->ssl;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_quic_max_ack_delay(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_msec_t *sp = data;\n\n    if (*sp >= 16384) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"quic_max_ack_delay\\\" must be less than 16384\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_quic_max_udp_payload_size(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *sp = data;\n\n    if (*sp < NGX_QUIC_MIN_INITIAL_SIZE\n        || *sp > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"quic_max_udp_payload_size\\\" must be between \"\n                           \"%d and %d\",\n                           NGX_QUIC_MIN_INITIAL_SIZE,\n                           NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_quic_host_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_quic_conf_t  *qcf = conf;\n\n    u_char           *buf;\n    size_t            size;\n    ssize_t           n;\n    ngx_str_t        *value;\n    ngx_file_t        file;\n    ngx_file_info_t   fi;\n\n    if (qcf->host_key.len) {\n        return \"is duplicate\";\n    }\n\n    buf = NULL;\n#if (NGX_SUPPRESS_WARN)\n    size = 0;\n#endif\n\n    value = cf->args->elts;\n\n    if (ngx_conf_full_name(cf->cycle, &value[1], 1) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n    file.name = value[1];\n    file.log = cf->log;\n\n    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 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_CONF_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 == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" zero key size\", &file.name);\n        goto failed;\n    }\n\n    buf = ngx_pnalloc(cf->pool, size);\n    if (buf == NULL) {\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    qcf->host_key.data = buf;\n    qcf->host_key.len = 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    return NGX_CONF_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    if (buf) {\n        ngx_explicit_memzero(buf, size);\n    }\n\n    return NGX_CONF_ERROR;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_quic_module.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STREAM_QUIC_H_INCLUDED_\n#define _NGX_STREAM_QUIC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\nextern ngx_module_t  ngx_stream_quic_module;\n\n\n#endif /* _NGX_STREAM_QUIC_H_INCLUDED_ */\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\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    { 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    { 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      ngx_conf_set_bitmask_slot,\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      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_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      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 (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    rc = ngx_ssl_handshake(c);\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_AGAIN) {\n        sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);\n\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\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\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    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|NGX_SSL_TLSv1\n                          |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));\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->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\n    conf->ssl.log = cf->log;\n\n    if (!conf->listen) {\n        return NGX_CONF_OK;\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 \\\"listen ... ssl\\\" directive in %s:%ui\",\n                      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 \\\"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\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 SSL_CTRL_SET_TLSEXT_HOSTNAME\n    SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,\n                                           ngx_stream_ssl_servername);\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\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    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\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) {\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_uint_t                    i;\n    ngx_stream_listen_t          *listen;\n    ngx_stream_handler_pt        *h;\n    ngx_stream_ssl_conf_t        *scf;\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_SSL_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_ssl_handler;\n\n    listen = cmcf->listen.elts;\n\n    for (i = 0; i < cmcf->listen.nelts; i++) {\n        if (!listen[i].quic) {\n            continue;\n        }\n\n        scf = listen[i].ctx->srv_conf[ngx_stream_ssl_module.ctx_index];\n\n        if (scf->certificates && !(scf->protocols & NGX_SSL_TLSv1_3)) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"\\\"ssl_protocols\\\" must enable TLSv1.3 for \"\n                          \"the \\\"listen ... quic\\\" directive in %s:%ui\",\n                          scf->file, scf->line);\n            return NGX_ERROR;\n        }\n    }\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} 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} 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_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(\"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_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        && !(c->type == SOCK_DGRAM && flush))\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"
  }
]